在观察夏令时的时区中,时钟通常为:
例如,在Europe/Paris
时区内,从夏天过渡到冬季,UTC偏移从+02:00
变为+01:00
,位于10月最后一个星期日的3:00 AM
。
换句话说:
在3:00 AM
上的+02:00
(2014-10-26
),时钟将重新设置为2:00 AM
(+01:00
)。
这意味着在DateTime
时区的2014-10-26
处为02:30 AM
创建Europe/Paris
是不明确的,因为它可以代表:
2014-10-26T02:30+01:00
(时间戳1414287000
)2014-10-26T02:30+02:00
(时间戳1414283400
)Java ZonedDateTime documentation很好地解释了这个问题,并且他们的API提供了一种在需要时选择首选偏移量的方法。
然而,在PHP中,似乎通过任意选择 winter 时间来解决这种歧义:
$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
(通过任意方式,我的意思是我找不到任何关于它的文档)。
从给定时区的DST重叠范围内的日期和时间创建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
时区中代表此日期/时间的对象?
答案 0 :(得分:2)
首先,请考虑PHP中的a known bug会对您产生影响。考虑:
$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
您可能还希望阅读this related question and answer。