【问题标题】:PHP DateTime->diff() doesn't work correctlyPHP DateTime->diff() 无法正常工作
【发布时间】:2019-07-12 12:52:34
【问题描述】:

我遇到了一个有趣的案例,与DateTime类的diff()方法有关。

如果我尝试计算两个日期之间的差异,例如

$datetime1 = new \DateTime('June 2019');
$datetime2 = new \DateTime('July 2019');
$interval = $datetime1->diff($datetime2);
echo $interval->format('%m');

,结果我得到0

为什么会这样?

Print_r 的:

$datetime1:

DateTime Object ( [date] => 2019-06-01 00:00:00.000000 
[timezone_type] => 3 [timezone] => Europe/Berlin )

$datetime2:

DateTime Object ( [date] => 2019-07-01 00:00:00.000000 
[timezone_type] => 3 [timezone] => Europe/Berlin )

$间隔:

DateInterval Object ( [y] => 0 [m] => 0 [d] => 30 [h] => 0 [i] => 0 [s] => 0 [f] => 0 
[weekday] => 0 [weekday_behavior] => 0 [first_last_day_of] => 0 
[invert] => 0 [days] => 30 [special_type] => 0 [special_amount] => 0
 [have_weekday_relative] => 0 [have_special_relative] => 0 )

【问题讨论】:

  • 奇怪,我得到1
  • 相差30天,所以只能算几个月。
  • @montie 我不知道,但如果有人有问题,而其他人没有,那就有问题了
  • 如果你添加 3 行你会得到什么? print_r($datetime1); print_r($datetime2); print_r($interval);请将结果作为额外数据添加到您的问题中
  • 这已经回答了。请看这个stackoverflow.com/questions/40354442/…

标签: php datetime


【解决方案1】:

PHP 中的时区和日期处理存在很大的不一致

这似乎是一个错误(就日期时间格式被强制为 GMT* 偏移量而言,according to this comment)。

*(但强制到 GMT 似乎与下面代码建立的结果不一致)

将服务器时区值设置为任何时区都不会影响此脚本时区异常。

以下两个案例展示了不同时区发生的情况:


案例一:

以下代码将输出每个时区的结果列表:

$tzList = DateTimeZone::listIdentifiers(DateTimeZone::ALL);

print "Current Zone:". print_r(ini_get('date.timezone'),true)."<br>\n<BR>\n";

foreach($tzList as $tzRow) {
    $tz = new DateTimeZone($tzRow);
    //$tz = null;
    $datetime1 = new \DateTime('June 2019', $tz);
    $datetime2 = new \DateTime('July 2019', $tz);
    $interval = $datetime1->diff($datetime2, false);
    echo $interval->format('%a %m') . PHP_EOL. " :: ";

    print print_r($datetime1->getTimezone(),true)."<BR>";
}

此列表输出的结果显示01 月的其余部分的比率很高(~60%)。

请看这里:http://sandbox.onlinephpfunctions.com/code/b18ba13deb94d112b12630a12265363fb6c7670b


案例2:

在创建对象后设置时区,得到一致的答案(尽管不正确)

$tzList = DateTimeZone::listIdentifiers(DateTimeZone::ALL);

print "Current Zone:". print_r(ini_get('date.timezone'),true)."<br>\n<BR>\n";

foreach($tzList as $tzRow) {
    //$tz = new DateTimeZone($tzRow);
    $tz = null;
    $datetime1 = new \DateTime('June 2019', $tz);
    $datetime2 = new \DateTime('July 2019', $tz);
    $datetime1->setTimezone(new DateTimeZone($tzRow));
    $datetime2->setTimezone(new DateTimeZone($tzRow));
    $interval = $datetime1->diff($datetime2, false);
    echo $interval->format('%a %m') . PHP_EOL. " :: ";

    print print_r($datetime1->getTimezone(),true)."<BR>";
}
 

此输出在此处生成所有所有 30 天;但所有相差 0 个月。

在此处查看代码:http://sandbox.onlinephpfunctions.com/code/7bcc62f4e36f41df71b9cb928de75a53f233d9fd


因此,如果您想使用有时正确的结果或普遍不正确但一致的结果,您可以选择通过设置 when 在 DateTime 对象中建立 Timezone 值。


可能的解决方案:

如果服务器时区正确设置为 UTC“正确”时区(在Case 1 中自然返回“1”月,则上述 CASE 2 在指定给 DateTime 的所有时区中始终有效对象。

【讨论】:

  • 另外,在这种情况下,另一个可行的解决方案是在两个参数中将具体日期放在月份和年份的前面。分别为:“2019 年 6 月 1 日”和“2019 年 7 月 31 日”。
【解决方案2】:

问题出在您的时区。

有一个帖子解释了它here

看这个例子:

<?php

echo "----- Europe/Berlin -----\n";
date_default_timezone_set('Europe/Berlin'); 
$datetime1 = new \DateTime('June 2019');
$datetime2 = new \DateTime('July 2019');
print_r($datetime1);
print_r($datetime2);

$interval = $datetime1->diff($datetime2);
print_r($interval);

echo "%m = " . $interval->format('%m') . PHP_EOL;
echo "%a = " . $interval->format('%a') . PHP_EOL;
echo "%s = " . $interval->format('%s') . PHP_EOL;


echo "\n\n\n----- America/Sao_Paulo -----\n";
date_default_timezone_set('America/Sao_Paulo'); 
$datetime1 = new \DateTime('June 2019');
$datetime2 = new \DateTime('July 2019');
print_r($datetime1);
print_r($datetime2);

$interval = $datetime1->diff($datetime2);
print_r($interval);

echo "%m = " . $interval->format('%m') . PHP_EOL;
echo "%a = " . $interval->format('%a') . PHP_EOL;
echo "%s = " . $interval->format('%s') . PHP_EOL;

还有输出:

$ php date_diff.php 
----- Europe/Berlin -----
DateTime Object
(
    [date] => 2019-06-01 00:00:00.000000
    [timezone_type] => 3
    [timezone] => Europe/Berlin
)
DateTime Object
(
    [date] => 2019-07-01 00:00:00.000000
    [timezone_type] => 3
    [timezone] => Europe/Berlin
)
DateInterval Object
(
    [y] => 0
    [m] => 0
    [d] => 30
    [h] => 0
    [i] => 0
    [s] => 0
    [f] => 0
    [weekday] => 0
    [weekday_behavior] => 0
    [first_last_day_of] => 0
    [invert] => 0
    [days] => 30
    [special_type] => 0
    [special_amount] => 0
    [have_weekday_relative] => 0
    [have_special_relative] => 0
)
%m = 0
%a = 30
%s = 0



----- America/Sao_Paulo -----
DateTime Object
(
    [date] => 2019-06-01 00:00:00.000000
    [timezone_type] => 3
    [timezone] => America/Sao_Paulo
)
DateTime Object
(
    [date] => 2019-07-01 00:00:00.000000
    [timezone_type] => 3
    [timezone] => America/Sao_Paulo
)
DateInterval Object
(
    [y] => 0
    [m] => 1
    [d] => 0
    [h] => 0
    [i] => 0
    [s] => 0
    [f] => 0
    [weekday] => 0
    [weekday_behavior] => 0
    [first_last_day_of] => 0
    [invert] => 0
    [days] => 30
    [special_type] => 0
    [special_amount] => 0
    [have_weekday_relative] => 0
    [have_special_relative] => 0
)
%m = 1
%a = 30
%s = 0

在我的时区$interval-&gt;format('%m'); 是 1。

您可以在日期上设置时区以计算它们之间的差异。

$datetime1 = new \DateTime('June 2019', new DateTimeZone('UTC'));
$datetime2 = new \DateTime('July 2019', new DateTimeZone('UTC'));
$interval = $datetime1->diff($datetime2);
print_r($interval);
echo "%m = " . $interval->format('%m') . PHP_EOL;

$ php date_diff.php 
DateInterval Object
(
    [y] => 0
    [m] => 1
    [d] => 0
    [h] => 0
    [i] => 0
    [s] => 0
    [f] => 0
    [weekday] => 0
    [weekday_behavior] => 0
    [first_last_day_of] => 0
    [invert] => 0
    [days] => 30
    [special_type] => 0
    [special_amount] => 0
    [have_weekday_relative] => 0
    [have_special_relative] => 0
)
%m = 1

【讨论】:

【解决方案3】:

您可以通过添加时区来尝试吗?

$timezones = [
    'UTC',
    'Europe/Berlin',
    'America/Belize',
    'Asia/Hong_Kong',
];

foreach ($timezones as $timezone) {
    $tz = new DateTimeZone($timezone);
    $datetime1 = new \DateTime('June 2019', $tz);
    $datetime2 = new \DateTime('July 2019', $tz);
    $interval = $datetime1->diff($datetime2);
    echo str_pad($timezone, 20, ' ').' '.$interval->format('months: %M,  day: %D,  days: %a') . PHP_EOL;
}

结果:

UTC                  months: 01,  day: 00,  days: 30
Europe/Berlin        months: 00,  day: 30,  days: 30
America/Belize       months: 01,  day: 00,  days: 30
Asia/Hong_Kong       months: 00,  day: 30,  days: 30

【讨论】:

  • UTC 结果为 1,而 Europe/Berlin 对我来说结果为 0
  • 使用 %a 作为格式将始终返回相同的值(以天为单位)
猜你喜欢
  • 1970-01-01
  • 2015-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-13
  • 2021-08-16
  • 2012-10-28
  • 2013-10-13
相关资源
最近更新 更多