在观测夏令时的时区中,时钟通常为:
- 在从冬季到夏季的过渡期间向前移动
- 在从夏季到冬季的过渡过程中倒退
例如,在Europe/Paris
时区中,UTC偏移量在10月最后一个星期日的3:00 AM
从夏季转换到冬季期间从+02:00
更改为+01:00
。
换句话说:
在2014-10-26
上的3:00 AM
(+02:00
)处,时钟被设置回2:00 AM
(+01:00
)。
这意味着在Europe/Paris
时区的02:30 AM
为2014-10-26
创建DateTime
是不明确的,因为它可以表示:
2014-10-26T02:30+01:00
(时间戳1414287000
)2014-10-26T02:30+02:00
(时间戳1414283400
)
Java的ZonedDateTime文档很好地解释了这个问题,并且他们的API提供了一种在需要时选择首选偏移的方法。
然而,在PHP中,这种模糊性似乎是通过任意选择冬季时间来解决的:
$dt = new DateTime('2014-10-26T02:30', new DateTimeZone('Europe/Paris'));
echo $dt->format(DateTime::ISO8601); // 2014-10-26T02:30:00+0100
echo $dt->getTimestamp(); // 1414287000
echo $dt->getOffset(); // 3600
echo $dt->getTimeZone->getName(); // Europe/Paris
(我所说的武断,是指我找不到任何关于它的文档)
在根据给定时区的夏令时重叠范围内的日期和时间创建DateTime
时,是否有方法选择首选偏移量
或者换句话说:
如何创建具有以下特征的DateTime
对象:
echo $dt->format(DateTime::ISO8601); // 2014-10-26T02:30:00+0200
echo $dt->getTimestamp(); // 1414283400
echo $dt->getOffset(); // 7200
echo $dt->getTimeZone->getName(); // Europe/Paris
也就是说,在夏季时间的Europe/Paris
时区中表示该日期/时间的对象?
首先,考虑一下PHP中有一个已知的错误会影响您。考虑:
$dt = new DateTime('2014-10-26T02:30', new DateTimeZone('Europe/Paris'));
echo $dt->format(DateTime::ISO8601) . " (" . $dt->getTimeStamp() . ")n";
$dt->setTimeStamp($dt->getTimeStamp() - 3600);
echo $dt->format(DateTime::ISO8601) . " (" . $dt->getTimeStamp() . ")n";
输出:
2014-10-26T02:30:00+0100 (1414287000)
2014-10-26T02:30:00+0100 (1414287000)
尽管您将时间戳向后调整了一个小时以反映夏季时间,PHP还是错误地将其提前到了冬季时间位置。
通过使用UTC作为中介,您可以绕过此进行显示。
$tz = new DateTimeZone('Europe/Paris');
$dt = new DateTime('2014-10-26T02:30', $tz);
echo $dt->format(DateTime::ISO8601) . " (" . $dt->getTimeStamp() . ")n";
$ts = $dt->getTimeStamp() - 3600;
$dt = new DateTime("@$ts", new DateTimeZone('UTC'));
$dt->setTimeZone($tz);
echo $dt->format(DateTime::ISO8601) . " (" . $dt->getTimeStamp() . ")n";
输出:
2014-10-26T02:30:00+0100 (1414287000)
2014-10-26T02:30:00+0200 (1414287000)
请注意,即使返回了错误的时间戳(应该是1414283400),它也会保留+0200的期望夏季时间偏移。
现在,让我们来解决知道何时应用这个问题。我们将检查转换,并使用它来决定是否减去一个小时。
// set up the original input values
$tz = new DateTimeZone('Europe/Paris');
$dt = new DateTime('2014-10-26T02:30', $tz);
echo $dt->format(DateTime::ISO8601) . "n";
// check for a transition +/- an hour from the current time stamp
$ts = $dt->getTimestamp();
$transitions = $tz->getTransitions($ts - 3600, $ts + 3600);
if (count($transitions) > 1) {
// see if we are moving backwards, creating the ambiguity
$shift = $transitions[1]['offset'] - $transitions[0]['offset'];
if ($shift < 0)
{
// apply the difference in offsets to move back to summer time
$ts = $ts + $shift;
$dt = new DateTime("@$ts", new DateTimeZone('UTC'));
$dt->setTimeZone($tz);
}
}
echo $dt->format(DateTime::ISO8601) . "n";
输出:
2014-10-26T02:30:00+0100
2014-10-26T02:30:00+0200
你也可以阅读这个相关的问题和答案。