3

我对两个日期时间的差异有疑问。这是显示 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)
}

编辑:第一个和第二个日期时间:

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 上,但我确信这个 DateInterval 几天前有 0 个月。DateInterval 在 PHP 5.4.28 和 5.5.14 中输出 0 天。我不确定PHP版本是否有效。

在这两种情况下,days 属性都是 92。

4

2 回答 2

4

深入了解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/ZurichUTC日期之后,分别是 now2014-02-28 23:00:002014-05-31 22:00:00for$a和 for 和$b


解决方案是完全在 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);

?>

请注意,所有的日子都是 now 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)

要深入了解这种现象,请注意以下几点:

  • 二月有 28 天(2014 年)
  • 五月有31天
  • DST 于 3 月 30 日开始:+01:00
  • 欧洲/苏黎世是 UTC+02:00

从欧洲/苏黎世转换为 UTC 时,不仅要考虑年、月和日,还要考虑小时、分钟和秒。如果这是任何一个月的第一天以外的任何一天,则不会发生此问题,但是时间仍然是 23:00 和 22:00(在上面的示例之前)。

于 2014-07-24T10:55:08.690 回答
2

这个东西就DateTimeZone看你提供了。

如果您设置Europe/Zurich或任何EEST时间,您将获得描述的结果。

如果GMT/UTC例如,你会得到$d = 0.


您可以在项目中使用全局时区定义来避免此类问题(如果适合您

date_default_timezone_set("欧洲/苏黎世");

DateTime或为对象定义所需的时区。


UPD:正如@mudasobwa在下面的评论中提到的那样,这个问题大约在 3 年前在这里提到过。

于 2014-07-24T10:34:55.533 回答