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

Posted

技术标签:

【中文标题】在 DateTime DST 重叠期间选择首选偏移量【英文标题】:Choose the preferred offset during a DateTime DST overlap 【发布时间】:2014-10-26 23:38:59 【问题描述】:

在遵守夏令时的时区,时钟通常:

在从冬天夏天的过渡期间向前移动 在从夏季冬季的过渡期间被退回

例如,在Europe/Paris 时区,在从summerwinter 的过渡期间,UTC 偏移量从+02:00 更改为+01:00,在3:00 AM 十月的最后一个星期日。

换句话说:

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时间戳14142870002014-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】:

首先,考虑到 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。

【讨论】:

以上是关于在 DateTime DST 重叠期间选择首选偏移量的主要内容,如果未能解决你的问题,请参考以下文章

通过 GMT 偏移值 DST 更改/转换 DateTime 是不是安全? [关闭]

Rails 使用时区参数和 DST 强制执行 DateTime 偏移

C# - 处理 DST 过渡日的主要时间范围 - 提供的 DateTime 表示无效时间

如何显示 dst 空闲时区偏移量?

如何解析相对时间?

由于 DST 时钟向前,perl DateTime 和不存在的时间用户输入