Php 增加时间的问题

Php 增加时间的问题,php,datetime,time,Php,Datetime,Time,我在计算时间的时候遇到了一个奇怪的时间偏差,这让我很困惑。或者,好吧,我已经找到了原因(或者至少是一个似是而非的原因),但不知道该怎么办,或者是否真的有什么可以做的 简单地说,问题是,当以大于1周的单位添加时间(不包括乘数)时,似乎不可能精确。确切地说,我的意思是,当我把1年的时间加在现在,我就结束了1年零几小时 我可以理解,增加1个月(或更多)就可以做到这一点,因为一个月的长度不同,但一年应该大致相同,不是吗 现在我知道您想知道我是如何做到这一点的,所以下面是(伪)代码: class My\u

我在计算时间的时候遇到了一个奇怪的时间偏差,这让我很困惑。或者,好吧,我已经找到了原因(或者至少是一个似是而非的原因),但不知道该怎么办,或者是否真的有什么可以做的

简单地说,问题是,当以大于1周的单位添加时间(不包括乘数)时,似乎不可能精确。确切地说,我的意思是,当我把1年的时间加在现在,我就结束了1年零几小时

我可以理解,增加1个月(或更多)就可以做到这一点,因为一个月的长度不同,但一年应该大致相同,不是吗

现在我知道您想知道我是如何做到这一点的,所以下面是(伪)代码:

class My\u DateInterval{
public$length;//间隔长度(秒)
公共函数构造($interval){
$this->length=0;
预赛(
(0-0-9[0-9[0-9[0-9[0-9[0-9[0 0-9[1,{1,,})M 124,((((((((([0-0-9[0-9[0-9[0-9[0-9[0-9[0-9[1[1[1[1,1,1,1,,,,,})))P((((((([0-0-0-9[0-9[0-9[0-9[0-9[0-9[0-9[0-9[0-9[0[0[0 0 0[1,1,{1,{1,,{1,,,,,})1,})1,})1,))))))))))))))))))))))))))))))W)W)W))W(((((((((}/',
$interval,$timeparts
);
如果(是数字($timeparts['years'])$this->length+=intval($timeparts['years'])*31556926;//1年(秒)
如果(是数值($timeparts['months'])$this->length+=intval($timeparts['months'])*2629743.83;//1个月(秒)
如果(是数值($timeparts['weeks'])$this->length+=intval($timeparts['weeks'])*604800;//以秒为单位的1周
如果(是数值($timeparts['days'])$this->length+=intval($timeparts['days'])*86400;//以秒为单位的1天
如果(是数值($timeparts['hours'])$this->length+=intval($timeparts['hours'])*3600;//1小时(秒)
如果(是数值($timeparts['minutes'))$this->length+=intval($timeparts['minutes'))*60;//1分钟(秒)
如果(是数值($timeparts['seconds'))$this->length+=intval($timeparts['seconds'));
$this->length=圆形($this->length);
}
}
类My_DateTime扩展了DateTime{
公共函数构造($time,$tz=null){
父项::u construct($time,$tz);
}
公共函数添加(My_DateInterval$i){
$this->modify($i->length.'seconds');
}
}
$d=新的My_DateTime();
$i=新的我的约会时间间隔('P1Y');
$d->add($i);
对间隔长度和前后值进行一些调试打印,从“它按预期工作并且所有检查结果”的意义上看,这一切都是好的,但上面提到的问题仍然存在:如果可能的话,我非常希望得到正确的结果

在这么多的话:如何做精确的数学与时间单位大于1周。(即1个月/1年)


对于那些想知道我为什么要使用我的类的人来说,这是因为PHP5.3还不够普及,但我正试图让迁移到内置实用程序类的过程尽可能顺利。

一年大约365.25天。因此我们有闰年

月份的长度是可变的

因此,添加一年和添加一个月的语义可能并不对应于添加固定的秒数

例如,2007年2月14日增加一个月可能会产生2007年3月14日,2008年2月14日则会产生2008年3月14日,分别增加28天或29天

这东西变得很粗糙,特别是当我们加入不同的日历时,世界上很多地方甚至没有二月!然后加上“七个工作日”——你需要考虑公共假日日历

你找不到这方面的图书馆吗

我可以理解,增加1个月(或更多)就可以做到这一点,因为一个月的长度不同,但一年应该大致相同,不是吗

在上面,当您添加一年的秒数时,您将从天数(我猜是)和平均年数(即365.2421..*24*60*60)开始。因此,您的计算隐含地将一年定义为特定天数

根据这一定义,12月31日的时间比6小时稍长一些。所以你的“时钟”从12月31日23:59到29:59(不管是什么),然后在1月1日滚动到00:00。月也会发生类似的事情,因为你也将它们定义为特定的秒数,而不是28-31天

如果您的计算目的是在通用“平均”日历上对事件之间的差异进行计时,那么您的方法可以正常工作。但是如果你需要一个真正的日历,它会关闭

最简单的方法是使用假儒略历。跟踪每个时间段(年、月、日等)。将天数精确定义为24小时。将一年定义为365天。如果一年可以被4整除,则加上1天,但如果一年可以被100整除,则不加1天,除非它也可以被400整除

当你想加减的时候,手动计算。。。每次递增时,检查“溢出”。如果您在当月的某一天溢出,请重置它并增加该月的值(然后检查该月是否溢出,等等)

您的日历将能够完全对应于一个真正的日历,几乎所有的事情

大多数计算机数据实现基本上都是这样做的(复杂程度各不相同)。例如,在javascript中(因为我的web检查器很方便):

正如你所看到的,这相当于365天的毫秒。没有混乱,没有大惊小怪

顺便说一句,争取时间是很难的。数学是以60为基数的。都是特殊情况(闰年、夏令时、时区)


玩得开心

为了防止有人感兴趣,工作解决方案非常简单(像往常一样),让我觉得自己很愚蠢。与将间隔长度转换为秒不同,更冗长的版本可以正确地使用PHP内部构件

class My_DateInterval {
    public $length; // strtotime supported string format
    public $years;
    public $months;
    public $weeks;
    public $days;
    public $hours;
    public $minutes;
    public $seconds;

    public function __construct($interval) {
        $this->length = 0;

        preg_match(
            '/P(((?<years>([0-9]{1,}))Y)|((?<months>([0-9]{1,}))M)|((?<weeks>([0-9]{1,}))W)|((?<days>([0-9]{1,}))D)){0,}(T((?<hours>([0-9]{1,2})){1}H){0,1}((?<minutes>([0-9]{1,2}){1})M){0,1}((?<seconds>([0-9]{1,2}){1})S){0,1}){0,1}/', 
            $interval, $timeparts
        );

        $this->years = intval($timeparts['years']);
        $this->months = intval($timeparts['months']);
        $this->weeks = intval($timeparts['weeks']);
        $this->days = intval($timeparts['days']);
        $this->hours = intval($timeparts['hours']);
        $this->minutes = intval($timeparts['minutes']);
        $this->seconds = intval($timeparts['seconds']);

        $length = $this->toString();
    }

    public function toString() {
        if (empty($this->length)) {
            $this->length =  sprintf('%d years %d months %d weeks %d days %d hours %d minutes %d seconds',
                $this->years,
                $this->months,
                $this->weeks,
                $this->days,
                $this->hours,
                $this->minutes,
                $this->seconds
            );
        }

        return $this->length;
    }
}
class My\u DateInterval{
P
> new Date(2010,0,1,0,0,0) - new Date(2009,0,1,0,0,0)
31536000000
class My_DateInterval {
    public $length; // strtotime supported string format
    public $years;
    public $months;
    public $weeks;
    public $days;
    public $hours;
    public $minutes;
    public $seconds;

    public function __construct($interval) {
        $this->length = 0;

        preg_match(
            '/P(((?<years>([0-9]{1,}))Y)|((?<months>([0-9]{1,}))M)|((?<weeks>([0-9]{1,}))W)|((?<days>([0-9]{1,}))D)){0,}(T((?<hours>([0-9]{1,2})){1}H){0,1}((?<minutes>([0-9]{1,2}){1})M){0,1}((?<seconds>([0-9]{1,2}){1})S){0,1}){0,1}/', 
            $interval, $timeparts
        );

        $this->years = intval($timeparts['years']);
        $this->months = intval($timeparts['months']);
        $this->weeks = intval($timeparts['weeks']);
        $this->days = intval($timeparts['days']);
        $this->hours = intval($timeparts['hours']);
        $this->minutes = intval($timeparts['minutes']);
        $this->seconds = intval($timeparts['seconds']);

        $length = $this->toString();
    }

    public function toString() {
        if (empty($this->length)) {
            $this->length =  sprintf('%d years %d months %d weeks %d days %d hours %d minutes %d seconds',
                $this->years,
                $this->months,
                $this->weeks,
                $this->days,
                $this->hours,
                $this->minutes,
                $this->seconds
            );
        }

        return $this->length;
    }
}