php date_diff 2021-12-01 而不是 UTC 时区的错误
php date_diff bug with 2021-12-01 and not UTC timezone
制作diff,然后将其应用于相同的日期,结果我们希望看到相同的日期,但结果可能无法预测。
已在 php 8.0.16、7.4.7
上测试
<?php
date_default_timezone_set('Europe/Amsterdam');
$dateTimeNow = new DateTimeImmutable('2022-03-18 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-18
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-16 but must be 2022-03-18!
$dateTimeNow = new DateTimeImmutable('2022-03-01 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-01
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-02 but must be 2022-03-01!
date_default_timezone_set('UTC');
$dateTimeNow = new DateTimeImmutable('2022-03-18 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-18
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-18 correct!
$dateTimeNow = new DateTimeImmutable('2022-03-01 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-01
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-01 correct!
注意。 在 php 8.1.3 上没有错误
如何解释,我应该用什么来确保它有效和稳定
所以我将您的代码重新排列为:
$tzs = [
new DateTimezone('Europe/Amsterdam'),
new DateTimezone('UTC')
];
$base = '2021-12-01 00:00:00.000000';
$dates = [
'2022-03-18 00:00:00.000000',
'2022-03-01 00:00:00.000000',
];
foreach( $tzs as $tz ) {
$basedate = new DateTimeImmutable($base, $tz);
foreach( $dates as $date ) {
$curdate = new DateTimeImmutable($date, $tz);
$diff = $basedate->diff($curdate);
$test = $basedate->add($diff);
printf("%s %s %5s/%4s %s %s\n",
$basedate->format('c'),
$curdate->format('c'),
$diff->format('%mm%dd'),
$diff->format('%ad'),
$test->format('c'),
($curdate == $test) ? '1' : '0'
);
}
}
在php>=8.1中输出:
2021-12-01T00:00:00+01:00 2022-03-18T00:00:00+01:00 3m17d/107d 2022-03-18T00:00:00+01:00 1
2021-12-01T00:00:00+01:00 2022-03-01T00:00:00+01:00 3m0d/ 90d 2022-03-01T00:00:00+01:00 1
2021-12-01T00:00:00+00:00 2022-03-18T00:00:00+00:00 3m17d/107d 2022-03-18T00:00:00+00:00 1
2021-12-01T00:00:00+00:00 2022-03-01T00:00:00+00:00 3m0d/ 90d 2022-03-01T00:00:00+00:00 1
并在 php <8.1:
2021-12-01T00:00:00+01:00 2022-03-18T00:00:00+01:00 3m15d/107d 2022-03-16T00:00:00+01:00 0
2021-12-01T00:00:00+01:00 2022-03-01T00:00:00+01:00 2m29d/ 90d 2022-03-02T00:00:00+01:00 0
2021-12-01T00:00:00+00:00 2022-03-18T00:00:00+00:00 3m17d/107d 2022-03-18T00:00:00+00:00 1
2021-12-01T00:00:00+00:00 2022-03-01T00:00:00+00:00 3m0d/ 90d 2022-03-01T00:00:00+00:00 1
DateTime->diff()
或 DateInterval
如何定义 'a month' 似乎有所不同。我敢打赌,这种差异与时区数据有关 and/or DST 转换,因此 UTC 和阿姆斯特丹 TZ 之间存在差异。
我的 go-to 解决方案始终是“始终以 UTC 格式存储和计算日期,所有其他时区仅供人类观察”。
但如果这不是一个可行的问题,那么对于这种特定情况,你没有处理比几天更细粒度的事情,你可能会逃脱与:
$diff = $old->diff($new);
$diff = new DateInterval($diff->format('P%aD'));
应该去掉含糊不清的“月份和剩余天数”,只留下“总天数”,这会在所有当前 PHP 版本中给出一致的结果。但是,如果在您的实际代码中有一个时间组件,那么您将不得不欺骗该格式调用以生成正确的间隔规范。
有 a laundry list of Date bugfixes in the 8.1 release,但我无法告诉您哪一个适用。
制作diff,然后将其应用于相同的日期,结果我们希望看到相同的日期,但结果可能无法预测。
已在 php 8.0.16、7.4.7
上测试<?php
date_default_timezone_set('Europe/Amsterdam');
$dateTimeNow = new DateTimeImmutable('2022-03-18 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-18
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-16 but must be 2022-03-18!
$dateTimeNow = new DateTimeImmutable('2022-03-01 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-01
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-02 but must be 2022-03-01!
date_default_timezone_set('UTC');
$dateTimeNow = new DateTimeImmutable('2022-03-18 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-18
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-18 correct!
$dateTimeNow = new DateTimeImmutable('2022-03-01 00:00:00.000000');
$dateTimeOld = new DateTimeImmutable('2021-12-01 00:00:00.000000');
$timeInterval = $dateTimeOld->diff($dateTimeNow);
var_dump($dateTimeNow); //output 2022-03-01
var_dump($dateTimeOld->add($timeInterval)); //output 2022-03-01 correct!
注意。 在 php 8.1.3 上没有错误
如何解释,我应该用什么来确保它有效和稳定
所以我将您的代码重新排列为:
$tzs = [
new DateTimezone('Europe/Amsterdam'),
new DateTimezone('UTC')
];
$base = '2021-12-01 00:00:00.000000';
$dates = [
'2022-03-18 00:00:00.000000',
'2022-03-01 00:00:00.000000',
];
foreach( $tzs as $tz ) {
$basedate = new DateTimeImmutable($base, $tz);
foreach( $dates as $date ) {
$curdate = new DateTimeImmutable($date, $tz);
$diff = $basedate->diff($curdate);
$test = $basedate->add($diff);
printf("%s %s %5s/%4s %s %s\n",
$basedate->format('c'),
$curdate->format('c'),
$diff->format('%mm%dd'),
$diff->format('%ad'),
$test->format('c'),
($curdate == $test) ? '1' : '0'
);
}
}
在php>=8.1中输出:
2021-12-01T00:00:00+01:00 2022-03-18T00:00:00+01:00 3m17d/107d 2022-03-18T00:00:00+01:00 1
2021-12-01T00:00:00+01:00 2022-03-01T00:00:00+01:00 3m0d/ 90d 2022-03-01T00:00:00+01:00 1
2021-12-01T00:00:00+00:00 2022-03-18T00:00:00+00:00 3m17d/107d 2022-03-18T00:00:00+00:00 1
2021-12-01T00:00:00+00:00 2022-03-01T00:00:00+00:00 3m0d/ 90d 2022-03-01T00:00:00+00:00 1
并在 php <8.1:
2021-12-01T00:00:00+01:00 2022-03-18T00:00:00+01:00 3m15d/107d 2022-03-16T00:00:00+01:00 0
2021-12-01T00:00:00+01:00 2022-03-01T00:00:00+01:00 2m29d/ 90d 2022-03-02T00:00:00+01:00 0
2021-12-01T00:00:00+00:00 2022-03-18T00:00:00+00:00 3m17d/107d 2022-03-18T00:00:00+00:00 1
2021-12-01T00:00:00+00:00 2022-03-01T00:00:00+00:00 3m0d/ 90d 2022-03-01T00:00:00+00:00 1
DateTime->diff()
或 DateInterval
如何定义 'a month' 似乎有所不同。我敢打赌,这种差异与时区数据有关 and/or DST 转换,因此 UTC 和阿姆斯特丹 TZ 之间存在差异。
我的 go-to 解决方案始终是“始终以 UTC 格式存储和计算日期,所有其他时区仅供人类观察”。
但如果这不是一个可行的问题,那么对于这种特定情况,你没有处理比几天更细粒度的事情,你可能会逃脱与:
$diff = $old->diff($new);
$diff = new DateInterval($diff->format('P%aD'));
应该去掉含糊不清的“月份和剩余天数”,只留下“总天数”,这会在所有当前 PHP 版本中给出一致的结果。但是,如果在您的实际代码中有一个时间组件,那么您将不得不欺骗该格式调用以生成正确的间隔规范。
有 a laundry list of Date bugfixes in the 8.1 release,但我无法告诉您哪一个适用。