我有一个问题,两个Datetime的差异。下面是显示DateInterval对象的命令行:
php -r "'$a = new Datetime('first day of 4 months ago midnight'); '$b = new Datetime('first day of 1 month ago midnight'); var_dump('$a->diff('$b));"
这里的DateInterval输出:
class DateInterval#3 (15) {
public $y => int(0)
public $m => int(3)
public $d => int(3)
public $h => int(0)
public $i => int(0)
public $s => int(0)
public $weekday => int(0)
public $weekday_behavior => int(0)
public $first_last_day_of => int(0)
public $invert => int(0)
public $days => int(92)
public $special_type => int(0)
public $special_amount => int(0)
public $have_weekday_relative => int(0)
public $have_special_relative => int(0)
}
Edit: The first and second Datetime:
class DateTime#1 (3) {
public $date =>
string(19) "2014-03-01 00:00:00"
public $timezone_type =>
int(3)
public $timezone =>
string(13) "Europe/Zurich"
}
class DateTime#2 (3) {
public $date =>
string(19) "2014-06-01 00:00:00"
public $timezone_type =>
int(3)
public $timezone =>
string(13) "Europe/Zurich"
}
注意这3天!我在PHP 5.5.8,但我敢肯定,这个日期间隔有0个月前几天。PHP 5.4.28和5.5.14版本的DateInterval输出为0天。我不确定PHP版本是否有影响。
在这两种情况下,days属性都是92
让我们深入了解Paul T. Rawkeen的答案,DateTime::diff
的问题在于它在计算之前首先将时区转换为UTC。
<?php
$zurich = new DateTimeZone('Europe/Zurich');
$utc = new DateTimeZone('UTC');
$a = new DateTime('first day of 4 months ago midnight',$zurich);
$b = new DateTime('first day of 1 month ago midnight',$zurich);
var_dump($a,$b);
$a->setTimezone($utc);
$b->setTimezone($utc);
var_dump($a,$b);
?>
给出如下:
object(DateTime)[3]
public 'date' => string '2014-03-01 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Zurich' (length=13)
object(DateTime)[4]
public 'date' => string '2014-06-01 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Zurich' (length=13)
object(DateTime)[3]
public 'date' => string '2014-02-28 23:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
object(DateTime)[4]
public 'date' => string '2014-05-31 22:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
3天的差异现在非常清楚,在时区从Europe/Zurich
转换为UTC
后,$a
和$b
的日期现在分别为2014-02-28 23:00:00
和2014-05-31 22:00:00
。
解决方案是完全在UTC中工作,并在显示DateTime之前进行转换:
<?php
$zurich = new DateTimeZone('Europe/Zurich');
$utc = new DateTimeZone('UTC');
$a = new DateTime('first day of 4 months ago midnight',$utc);
$b = new DateTime('first day of 1 month ago midnight',$utc);
var_dump($a,$b);
$a->setTimezone($zurich);
$b->setTimezone($zurich);
var_dump($a,$b);
?>
请注意,所有的日子现在都是01
,尽管时间现在有点不同(参见本回答末尾的注释):
object(DateTime)[3]
public 'date' => string '2014-03-01 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
object(DateTime)[4]
public 'date' => string '2014-06-01 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'UTC' (length=3)
object(DateTime)[3]
public 'date' => string '2014-03-01 01:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Zurich' (length=13)
object(DateTime)[4]
public 'date' => string '2014-06-01 02:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Zurich' (length=13)
要深入了解这种现象,请注意以下内容:
- 2月有28天(2014年)
- 5月31天
- 夏时制开始于3月30日:+01:00
- 欧洲/苏黎世为UTC+02:00
当从欧洲/苏黎世转换到UTC时,不仅要考虑年、月和日,还要考虑小时、分和秒。如果这一天不是每个月的第一天,则不会发生此问题,但是时间仍然是23:00和22:00(在上面的示例之前)。
这取决于您提供的DateTimeZone
。
如果您设置Europe/Zurich
或任何EEST
时间,您将得到所描述的结果。
如果GMT
/UTC
for example,你将得到$d = 0
。
您可以在项目中使用全局时区定义来避免此类问题(如果适合的话)
date_default_timezone_set("Europe/Zurich");
或定义DateTime
对象所需的时区。
UPD:正如@mudasobwa在下面的评论中提到的,这个问题在这里大约三年前就提到了