【问题标题】:PHP's date_diff() is behaving unexpectedlyPHP 的 date_diff() 行为异常
【发布时间】:2020-10-14 12:12:25
【问题描述】:

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

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

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

对象(DateInterval)#3 (15)
{ ["y"]=> int(0)
["m"]=> int(1) ["d"]=> int(0)
["h"]=> int(0)
["i"]=> int(0)
["s"]=> int(0)
["工作日"]=> int(0)
["weekday_behavior"]=> int(0)
["first_last_day_of"]=> int(0)
[“反转”]=> int(0)
["天"]=> 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-&gt;m 是 zer0。

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

【问题讨论】:

  • PHPFiddle 运行 5.6.40
  • 我想说您在较新的 PHP 中发现了一个错误。具有两个不同月份值的日期之间的差异应返回具有非零月份字段的间隔...
  • FWIW,表示“月”中的任何时间增量都被定义破坏,因为一个月的长度没有单一的定义值...... :) 不是你的错,主要是PHP 的错……
  • 现在,这不是真的。日历月的长度不是固定不变的,但它在日常生活中无处不在。连续几个月的同一日期之间的差异是一个月的差异,无论是 28、29、30 还是 31 天。能够根据date_diff 返回的这些分解值计算增量非常有用,因为日历月(和年)不会转换为固定的天数;如果所有月份的长度都相同,您可以以天为单位得到增量并除以。
  • @deceze:我学习日历是一种爱好;你没有告诉我任何我不知道的事情。 :) 我只是说,在日常生活中,我们有合同、租金和信用卡计费间隔以及以月为单位指定的东西。边缘情况将是边缘情况,但实用程序仍然存在。

标签: php


【解决方案1】:

可能区别在于时区。

试试这个:

<?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);       

【讨论】:

  • 当我读到“可能是时区”时,我嘲笑(我认为的)你的天真,因为它们都有相同的时区。然后我的下巴掉了下来,因为添加`date_default_timezone_set('UTC'); 实际上确实导致了一个月的差异。我不声称理解这一点,但感谢您的回答:-)
【解决方案2】:

是的,这是一个错误,与您的服务器时区有关。您可以使用此解决方法:

   $startDate = date_create('2019-11-01 UTC');
   $endDate = date_create('2019-12-01 UTC');
       
   $interval = date_diff($startDate, $endDate);
    
   echo $interval->m; //1     

【讨论】:

    【解决方案3】:

    您似乎在 PHP 7.3.11 中遇到了一个在 PhpFiddle 使用的旧版本中不存在的错误。它还修复了最新的稳定版本 7.4.11,因此您应该升级本地 PHP。

    (我注意到 7.3.11 是 MacOS 附带的;如果您使用的是 Mac,则可以使用 Homebrew 安装更新的 PHP。)

    【讨论】:

    • 我将 (XAMPP) 更新到 7.4.11 并且仍然得到相同的结果。
    • 奇怪,一定是我们不同的时区。我在 7.3.11 中得到了与你相同的行为,但在 7.4.11 中得到了预期的 1 个月增量。但听起来你无论如何都得到了答案,太棒了!
    • 不,时区是相同的。如果你愿意,我可以var_dump()。两者都在 TZ 3(柏林)——这让我感到困惑——当 UTC 工作时(赞成)
    • 不,我并没有质疑您的时区是否相同。我只是猜测您仍然在 7.4.11 中看到该错误而我没有看到的原因可能是因为 youI 位于不同的时区。但柏林不是 UTC+2(即将成为 UTC+1)吗? “TZ 3”是什么意思?
    • 当我评估时,我得到$startDate = date : 2019-11-01 00:00:00.000000, timezone : Europe/Berlin, timezone_type : 3,所以我想我错了:timezone_type(无论 3 是什么意思)。 Anyhoo,谢谢你的回答
    猜你喜欢
    • 1970-01-01
    • 2011-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多