【问题标题】:DateTimeZone and UTC offset for Standard and Daylight Times标准时间和夏令时的 DateTimeZone 和 UTC 偏移量
【发布时间】:2014-12-14 17:15:09
【问题描述】:

我无法从 PHP DateTimeZone 和基础数据库中获取 UTC 偏移信息。我对每个 DT/ST 对都很接近,但通常少一小时 (DT) 的时区不是。

例如(source):

东部夏令时间 (EDT),当观察夏令时(春季/夏季)时,比协调世界时 (UTC−04:00) 晚 4 小时)。

东部标准时间 (EST) 观察标准时间(秋季/冬季)时比协调世界时 (UTC−05:00强>)。

到目前为止,我在以下代码中获得了 UTC 偏移量(在 DateTimeZone::getOffset 中,它是针对 GMT 说的,但我认为这是相同的):

$zones = ['EDT', 'EST'];

foreach ($zones as $zone) {
    // get offset
    $timezone = new DateTimeZone($zone);
    $offset = $timezone->getOffset(new DateTime(null, $timezone));

    printf("%s (%s) %s\n", $zone, $timezone->getName(), format_offset($offset));
}

示例输出(full example,我使用 PHP 5.6.1RC1):

EDT (EDT) -0500
EST (EST) -0500

这两个时区实际上是典型的,我也想和其他时区一起做,但是我不想硬编码偏移量,而是想用 PHP 的 DateTimeZone 来解决这个问题。

当我看到output across multiple PHP versions 时,我可能会在这里遇到一个错误,看起来 HHVM 通过转置它没有这个问题:

Output for hhvm-3.2.0 - 3.3.0

EDT (America/New_York) -0400
EST (EST) -0500

Output for 5.5.10 - 5.6.2, php7@20140507 - 20141001

EDT (EDT) -0500
EST (EST) -0500

Output for 5.2.0 - 5.5.9

EDT (America/New_York) -0400
EST (America/New_York) -0400

有没有跨PHP版本兼容的方式?通常不会对DateTimeZone 进行那么深的摆弄,到目前为止我的印象是它会很好用。

【问题讨论】:

  • 不确定这是否会有所帮助,但可以确定是否正在应用 DST:6155224/php-daylight-saving-time-detection
  • @RyanVincent:是的,我认为这是相关的。问题似乎是 EST 和 EDT 不是 PHP 使用的时区数据库中的时区。他们也不会抛出异常,所以最后我还是需要保留一个列表。

标签: php datetime timezone utc timezone-offset


【解决方案1】:

PHP 使用标准IANA tz database。您可以在on Wikipedia 或in the PHP documentation 中找到支持的时区列表。

特别是,您会发现像"EST" 这样的值只是为了向后兼容。您可以在文档的this page 中看到,有一个关于 not 使用该格式的时区的大红色警告。虽然"EST" 在已弃用区域列表中,但"EDT" 不在。

一般来说,时区缩写很难识别,因为歧义太多。考虑this list of abbreviations,它对 EDT 有两种不同的解释,对 CST 有 5 种不同的解释,以及许多其他冲突。

还要考虑像 "America/New_York" 这样的完整标识符准确地表示 EST 和 EDT,并且知道何时在它们之间转换 - 包括历史差异。

在您提供的代码中,您要求 PHP 分别获取 EST 和 EDT 的 current 偏移量。这没有任何意义,因为 EDT 仅在一年中的部分时间有效,而 EST 和 EDT 不能在同一地点同时生效。而是使用"America/New_York" 偏移量,并提供特定时间来获取当时的偏移量。

如果您只需要每个区域的标准和日光偏移,您可以在当年的 1 月 1 日和 7 月 1 日午夜测试。以较低者为准。如果它们相同,则(通常)时区不使用 DST。

【讨论】:

  • 我还尝试了其他日期,例如 2014-01-01 和 2014-06-30 - 没有任何区别,也不适用于 EDT。我假设 DateTimeZone UTC 偏移量提供标准时间而不是夏令时。
  • 我很确定 "EDT" 本身不是与 DateTimeZone 一起使用的有效输入。反正它不在the list。
  • 那么它真正的意义在于您从哪里获得实际应用程序中的时区列表?如果您调用DateTimeZone::listIdentifiers,获得向后兼容区域的唯一方法是通过传递DateTimeZone::ALL_WITH_BC 参数显式请求它们。
【解决方案2】:

并非所有 PHP 的 DateTimeZone 接受作为 ctor 的 $timezone 参数中的字符串的时区实际上都具有稳定的 UTC 偏移量。从技术上讲,这两个 EDT 和 EST 并不是真正的时区,时区是东部时区( ET),即tz database 中的 US/Eastern 或 America/New_York,这是 PHP 的 DateTimeZone 运行的数据源.

+------------------+--------------------+-----------------------------------------+
| string           | getName()          | offsets                                 |
+------------------+--------------------+-----------------------------------------+
| America/New_York | (America/New_York) | -0500 / -0400 (2012-01-01 / 2012-06-30) |
| US/Eastern       | (US/Eastern)       | -0500 / -0400 (2012-01-01 / 2012-06-30) |
| EDT              | (EDT)              | -0500 / -0500 (2012-01-01 / 2012-06-30) |
| EST              | (EST)              | -0500 / -0500 (2012-01-01 / 2012-06-30) |
+------------------+--------------------+-----------------------------------------+

所以特别是我的问题中的这两个 EDT 和 EST 不能很好地工作,因为它们代表标准和夏令时分开,而 PHP 的 DateTimeZone 确实代表两者。因此,UTC 偏移量是不明确的。 PHP manual documents 仅出于向后兼容的原因而被接受,它们不应与 DateTimeZone 类一起使用。页面上的用户注释说得很好:

不要使用“EST”,至少在 PHP 5.3.3 中它与“EST5EDT”相同,而不是严格的标准时间。我发现将时间解释为标准时间的唯一可靠方法是使用相对于 UTC 的格式,例如:

$dateObject = date_create("2013-06-30 07:00:00-0500");

这也是我使用的规范(4.3。过时的日期和时间;RFC2822 p. 32)所说的,使用这些区域已经过时了。我只是希望我可以避免创建时区到 UTC 偏移地图:

EDT is semantically equivalent to -0400
EST is semantically equivalent to -0500
CDT is semantically equivalent to -0500
CST is semantically equivalent to -0600
MDT is semantically equivalent to -0600
MST is semantically equivalent to -0700
PDT is semantically equivalent to -0700
PST is semantically equivalent to -0800

但这并不是一个很大的列表。

【讨论】:

  • 如果您只考虑北美,那么少量的缩写就可以使用。一旦你开始考虑整个世界,it all falls apart。如果你可以保证你正在解析一个 RFC822/1123/2822 时间戳,你可以使用那些(并且只有那些)缩写。但大多数时候,你不应该指望这一点。
  • 是的,通常只有这些。我问的部分问题是 RFC2822 建议在带外信息可靠的情况下也接受其他值。有使用 PHP 的 DateTimeZone 的想法,但事实证明它在这方面做得并不好(解析器实际上可以接受任何可能是 TZ 的东西,因为 RFC2822 命名了常见的 3- 5 个 alpha,所以祝你好运,但那不是一个选择,尤其是这 8 个)。所以我放弃了。
  • 请记住,RFC822/1123/2822 和相关格式已经很老了。它们仍在被 HTTP 和电子邮件等很多东西使用,但大多数现代系统使用 ISO8601,通常是 RFC3339 中描述的配置文件。除非您出于某种原因绝对必须使用它,否则您不应该使用它。
  • RFC3339 (2002) 涉及到对 RFC2822 (2001) 的适当扩展,并且在 RFC822 上有很好的讨论材料,尤其是。为什么这些区域打得不好。感谢您的提示。对于该配置文件,我在管道中有另一个解析器,因为 PHP 也无法解析这些时间戳。
猜你喜欢
  • 2021-02-09
  • 2015-05-11
  • 2016-02-06
  • 2015-11-29
  • 1970-01-01
  • 2012-04-07
  • 1970-01-01
  • 2010-11-18
  • 2016-09-05
相关资源
最近更新 更多