在DateTime DST重叠期间选择首选偏移量

时间:2014-09-02 16:15:27

标签: php datetime dst

在观察夏令时的时区中,时钟通常为:

  • 在从冬天过渡到夏天
  • 的过程中前进
  • 在从夏天过渡到冬天的过程中被设置回来

例如,在Europe/Paris时区内,从夏天过渡到冬季,UTC偏移从+02:00变为+01:00 ,位于10月最后一个星期日的3:00 AM

换句话说:

3:00 AM上的+02:002014-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时区中代表此日期/时间的对象?

1 个答案:

答案 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