首页 > 解决方案 > PHP 的 date_diff() 行为异常

问题描述

可能只是我笨,但是...

<?php
   $startDate = date_create('2019-11-01');
   $endDate = date_create('2019-12-01');
       
    $interval = date_diff($startDate, $endDate);
    
   var_dump($interval);       
?>

PHPfiddle中给出(如预期的那样):

object(DateInterval)#3 (15)
{ ["y"]=> int(0)
["m"]=> int(1) <=== 这就是我在本地机器上遇到问题的原因
["d" ]=> int(0)
["h"]=> int(0)
["i"]=> int(0)
["s"]=> int(0)
["weekday"]=> int(0 )
["weekday_behavior"]=> int(0)
["first_last_day_of"]=> int(0)
["invert"]=> int(0)
["days"]=> int(30)
["special_type"] => int(0)
["special_amount"]=> int(0)
["have_weekday_relative"]=> int(0)
["have_special_relative"]=> int(0) }

查看 的值m,即月数。

但是,在我的本地机器上,相同的代码给出:

class DateInterval#4 (16) {
  public $y =>
  int(0)
  public $m =>
  int(0)                          <======  why ??!!
  public $d =>
  int(30)
  public $h =>
  int(0)
  public $i =>
  int(0)
  public $s =>
  int(0)
  public $f =>
  double(0)
  public $weekday =>
  int(0)
  public $weekday_behavior =>
  int(0)
  public $first_last_day_of =>
  int(0)
  public $invert =>
  int(0)
  public $days =>
  int(30)
  public $special_type =>
  int(0)
  public $special_amount =>
  int(0)
  public $have_weekday_relative =>
  int(0)
  public $have_special_relative =>
  int(0)
}

好的,我承认是 30 天,但我正在寻找的答案是 1 个月,并且$interval->m是 zer0。

我错过了什么?我不知道PHPfiddle网站使用的是什么版本的 PHP,但我在本地使用的是 7.3.11。

标签: php

解决方案


可能差异在于时区。

试试这个:

<?php
   date_default_timezone_set('UTC');

   $startDate = date_create('2019-11-01');
   $endDate = date_create('2019-12-01');
       
   $interval = date_diff($startDate, $endDate);
    
   var_dump($interval);  
/*
object(DateInterval)#3 (16) {
  ["y"]=>
  int(0)
  ["m"]=>
  int(1)
  ...
*/

也试试这个:

<?php
   $startDate = date_create('2019-11-01 23:00');
   $endDate = date_create('2019-12-01 23:00');
       
   $interval = date_diff($startDate, $endDate);
    
   var_dump($interval);       
   /* outputs the same ["m"]=> int(1) as above */

可能 date_diff 寻找 UTC 时间戳之间的差异,并且您将 PHP 上的默认时区设置为某个正值。

假设您有一些默认时区,即 UTC+02:00,因此 date_create('2019-11-01') 实际上日期为 2019-10-31 22:00:00 (UTC)date_create('2019-12-01')实际上日期为 2019-11-30 22:00:00 (UTC)

现在你可以看到他们没有整月的差异。

但你可以捕捉到另一个有趣的效果:

<?php
   //assuming your timezone is UTC+something
   $startDate = date_create('2019-10-31'); // creates a date 2019-10-30 XX:00 UTC
   $endDate = date_create('2019-12-01');   // creates a date 2019-11-30 XX:00 UTC
       
   $interval = date_diff($startDate, $endDate);
    
   var_dump($interval);       

/*
object(DateInterval)#3 (16) {
  ["y"]=>
  int(0)  
  ["m"]=>
  int(1)   sic(!)
  ["d"]=>
  int(0)   sic(!)
*/

如果您的时区有夏令时,您也可能会得到意想不到的结果。

<?php
   // Germany has the daylight saving shift at last Sunday of March and October.
   date_default_timezone_set('Europe/Berlin'); 

   $startDate = date_create('2020-03-28 03:00');
   $endDate = date_create('2020-03-29 02:00');

   $interval = date_diff($startDate, $endDate);
    
   var_dump($interval);  //   ["d"]=>  int(1)  ["h"]=>  int(0)

   
   
   $startDate = date_create('2020-03-28 03:00');
   $endDate = date_create('2020-03-29 03:00');

   $interval = date_diff($startDate, $endDate); 

   var_dump($interval); //   ["d"]=>  int(1)  ["h"]=>  int(0)


   $startDate = date_create('2020-03-28 03:00');
   $endDate = date_create('2020-03-28 03:00'); // the same
   $endDate->modify('+23 hour');

   $interval = date_diff($startDate, $endDate);
    
   var_dump($interval);  //   ["d"]=>  int(1)  ["h"]=>  int(0)

PHPFiddle将默认时区设置为 UTC,因此默认情况下会给出一个月的差异。

为避免这种情况,只需从头开始以 UTC 执行所有日期计算:

<?php
   $timeZone = new DateTimeZone('UTC');

   $startDate = new DateTime('2019-10-31', $timeZone);
   $endDate = new DateTime('2019-12-01', $timeZone);
       
   $interval = date_diff($startDate, $endDate);
    
   var_dump($interval);       

推荐阅读