上周日,欧盟从CET(+0100)转为CEST(+0200)。我正在编写代码以将增量应用于日期并且它无法正常工作,因为时区转换仅适用于某些相对格式:
'+x minutes'
省略了缺失的小时'+x hours'
不这是我的测试代码:
echo 'Time zone database: ' . timezone_version_get() . PHP_EOL;
echo PHP_EOL;
date_default_timezone_set('Europe/Madrid');
$start = new DateTime('2017-03-26 01:59:00');
$increments = array(
'+2 minutes' => '2017-03-26 03:01:00',
'+2 hours' => '2017-03-26 04:59:00',
);
echo 'Start: ' . $start->format('r') . PHP_EOL;
foreach ($increments as $increment => $expected_string) {
echo '>>> ' . $increment . PHP_EOL;
$expected_end = new DateTime($expected_string);
$actual_end = clone $start;
$actual_end->modify($increment);
echo 'Expected end: ' . $expected_end->format('r') . PHP_EOL;
echo 'Actual end: ' . $actual_end->format('r') . PHP_EOL;
echo ($expected_end->format('c')===$actual_end->format('c') ? 'OK' : 'ERROR') . PHP_EOL;
echo PHP_EOL;
}
Time zone database: 2016.3
Start: Sun, 26 Mar 2017 01:59:00 +0100
>>> +2 minutes
Expected end: Sun, 26 Mar 2017 03:01:00 +0200
Actual end: Sun, 26 Mar 2017 03:01:00 +0200
OK
>>> +2 hours
Expected end: Sun, 26 Mar 2017 04:59:00 +0200
Actual end: Sun, 26 Mar 2017 03:59:00 +0200
ERROR
由于relative formats通常是如此反直觉,我不确定我是否会收到一些记录在案的行为,或者这是一个错误。
你能说清楚吗?
答案 0 :(得分:0)
它可能不是一种相对格式的误解,因为行为在同一格式下是不稳定的:
date_default_timezone_set('Europe/Madrid');
$start = new DateTime('2017-03-26 01:59:00');
$increments = array(
'+60 minutes' => '2017-03-26 03:59:00',
'+61 minutes' => '2017-03-26 04:00:00',
);
echo 'Start: ' . $start->format('r') . PHP_EOL;
foreach ($increments as $increment => $expected_string) {
echo '>>> ' . $increment . PHP_EOL;
$expected_end = new DateTime($expected_string);
$actual_end = clone $start;
$actual_end->modify($increment);
echo 'Expected end: ' . $expected_end->format('r') . PHP_EOL;
echo 'Actual end: ' . $actual_end->format('r') . PHP_EOL;
echo ($expected_end->format('c')===$actual_end->format('c') ? 'OK' : 'ERROR') . PHP_EOL;
echo PHP_EOL;
}
Start: Sun, 26 Mar 2017 01:59:00 +0100
>>> +60 minutes
Expected end: Sun, 26 Mar 2017 03:59:00 +0200
Actual end: Sun, 26 Mar 2017 03:59:00 +0200
OK
>>> +61 minutes
Expected end: Sun, 26 Mar 2017 04:00:00 +0200
Actual end: Sun, 26 Mar 2017 03:00:00 +0200
ERROR
换句话说,添加61分钟会产生比添加60更早的日期。
简而言之, PHP无法正确处理时区转换。有一个issue ticket承认它,甚至2011年的RFC分析可能的修复。
(此信息将转至@Alex Blex。)
值得注意的是,基于Unix时间戳的旧功能也会受到影响:
<?php
date_default_timezone_set('Europe/Madrid');
$start = strtotime('2017-03-26 01:59:00');
$increments = array(
'+60 minutes' => '2017-03-26 03:59:00',
'+61 minutes' => '2017-03-26 04:00:00',
);
echo 'Start: ' . date('r', $start) . PHP_EOL;
foreach ($increments as $increment => $expected_string) {
echo '>>> ' . $increment . PHP_EOL;
$expected_end = strtotime($expected_string);
$actual_end = strtotime($increment, $start);
echo 'Expected end: ' . date('r', $expected_end) . PHP_EOL;
echo 'Actual end: ' . date('r', $actual_end) . PHP_EOL;
echo ($expected_end===$actual_end ? 'OK' : 'ERROR') . PHP_EOL;
echo PHP_EOL;
}
Start: Sun, 26 Mar 2017 01:59:00 +0100
>>> +60 minutes
Expected end: Sun, 26 Mar 2017 03:59:00 +0200
Actual end: Sun, 26 Mar 2017 03:59:00 +0200
OK
>>> +61 minutes
Expected end: Sun, 26 Mar 2017 04:00:00 +0200
Actual end: Sun, 26 Mar 2017 03:00:00 +0200
ERROR
当然使用UTC:)
您可以在内部使用UTC进行所有计算,也可以在执行日期数学之前切换到UTC。后者(最详细的案例)意味着:
<?php
date_default_timezone_set('Europe/Madrid');
$start = new DateTime('2017-03-26 01:59:00');
$increments = array(
'+60 minutes' => '2017-03-26 03:59:00',
'+61 minutes' => '2017-03-26 04:00:00',
);
echo 'Start: ' . $start->format('r') . PHP_EOL;
$local = $start->getTimezone();
$utc = new DateTimeZone('UTC');
foreach ($increments as $increment => $expected_string) {
echo '>>> ' . $increment . PHP_EOL;
$expected_end = new DateTime($expected_string);
$actual_end = clone $start;
$actual_end->setTimezone($utc);
$actual_end->modify($increment);
$actual_end->setTimezone($local);
echo 'Expected end: ' . $expected_end->format('r') . PHP_EOL;
echo 'Actual end: ' . $actual_end->format('r') . PHP_EOL;
echo ($expected_end->format('c')===$actual_end->format('c') ? 'OK' : 'ERROR') . PHP_EOL;
echo PHP_EOL;
}
Start: Sun, 26 Mar 2017 01:59:00 +0100
>>> +60 minutes
Expected end: Sun, 26 Mar 2017 03:59:00 +0200
Actual end: Sun, 26 Mar 2017 03:59:00 +0200
OK
>>> +61 minutes
Expected end: Sun, 26 Mar 2017 04:00:00 +0200
Actual end: Sun, 26 Mar 2017 04:00:00 +0200
OK
如果你在任何地方都使用UTC,那么在向最终用户展示时,只需要最后->setTimezone()
。