简体   繁体   English

PHP DateTime-> diff()无法正常工作

[英]PHP DateTime->diff() doesn't work correctly

I met an interesting case, related to the diff() method of DateTime class. 我遇到了一个有趣的案例,与DateTime类的diff()方法有关。

If I try to calculate difference between two dates in months like 如果我尝试计算两个月之间的差异,比如

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

, as result I get 0 . 结果我得到0

Why does this happen? 为什么会这样?

Print_r's: print_r的公司:

$datetime1: $ datetime1:

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

$datetime2: $ DATETIME2:

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

$interval: $间隔:

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 )

There is big inconsistency with timezone and date handing in PHP PHP中的时区和日期处理存在很大的不一致

This appears to be a bug (in so far as the datetime format is forced to a GMT* offset, according to this comment ). 这似乎是一个错误( 根据此评论 ,日期时间格式被强制为GMT *偏移)。

*(but forcing to GMT seems inconsistent with the results established by the code below) *(但强迫GMT似乎与下面代码建立的结果不一致)

Setting the server timezone value to any timezone does not effect this script timezone anomaly. 将服务器时区值设置为任何时区不会影响此脚本时区异常。

Below are two cases showing what happens in different time zones: 以下两个案例显示了不同时区的情况:


Case 1: 情况1:

The following code will output a list of results for each time zone: 以下代码将输出每个时区的结果列表:

$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>";
}

The result of this list output shows a high (~60%) rate of 0 and the rest of 1 month . 此列表输出的结果显示高(~60%)的速率为0 ,其余为1个月。

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


Case 2: 案例2:

Setting the timezone AFTER creating the object, results in a consistent answer (albeit incorrect) 在创建对象后设置时区会产生一致的答案(尽管不正确)

$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>";
}

This output's generated here all all 30 days out; 这些输出全部在 30天内生成; but all 0 months difference. 所有 0个月的差异。

See code here: http://sandbox.onlinephpfunctions.com/code/7bcc62f4e36f41df71b9cb928de75a53f233d9fd 请参阅此处的代码: http//sandbox.onlinephpfunctions.com/code/7bcc62f4e36f41df71b9cb928de75a53f233d9fd


So it's your choice if you want to use sometimes correct results or universally incorrect rbut consistent results, by setting when you establish the Timezone value in the DateTime objects. 因此,如果您希望使用有时正确的结果或普遍不正确的rbut一致结果,通过在DateTime对象中建立Timezone值进行设置这是您的选择。


Possible Solution: 可能解决方案

If the server timezone is correctly set to UTC "correct" timezone (that naturally returns "1" month in Case 1 , then CASE 2 above works consistently across all time zones given to the DateTime objects. 如果服务器时区正确设置为UTC“正确”时区(在Case 1中自然返回“1”月),则上面的CASE 2在给予DateTime对象的所有时区中一致地工作。

Can you try it by adding a timezone? 你可以通过添加时区来尝试吗?

$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;
}

Result: 结果:

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

The problem is in your timezone. 问题出在您的时区。

There is a post explaining about it here . 有一个文章,解释一下这里

See this example: 看这个例子:

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

And the output: 并输出:

$ 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

In my timezone $interval->format('%m'); 在我的时区$interval->format('%m'); is 1. 是1。

You can set timezone on your dates to calculate the difference between them. 您可以在日期上设置时区以计算它们之间的差异。

$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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM