2

我在我的项目中使用 DateTime(从 UTC 到欧洲/维也纳)转换所有日期。现在我有超过 2038 的日期,并且无法获得正确的时间。

示例代码:

$met = new DateTimeZone('Europe/Vienna');
$utc = new DateTimeZone('UTC');

$date = new DateTime('2043-08-04 08:00:00', $utc);
$date->setTimezone($met);
echo $date->format('Y-m-d H:i:s'); // Output is: 2043-08-04 09:00:00 instead of 2043-08-04 10:00:00

在 2043-03-29 和 2043-10-25 之间,由于“夏令时”,从 UTC 开始计算 +2 小时。

如果我将 2043-08-04 08:00:00 更改为 2037-08-04 08:00:00 我得到正确的时间。

我知道这是整数 32 位的 2038 问题,但是如何将整数 64 位与 DateTime setTimezone 函数一起使用?

谢谢

4

2 回答 2

3

如果该问题与溢出的 32 位 Unix 时间戳有关,那么您将得到 1970 年左右的日期以及类似的完全不正确的结果。但是,您的结果仅相差一小时。事实上,我们可以争辩说小时是正确的,因为你只是得到一个错误的时区偏移量,你可以看到你是否打印时区信息:

var_dump($date);
echo $date->format('c');
object(DateTime)#3 (3) {
  ["date"]=>
  string(26) "2043-08-04 09:00:00.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(13) "Europe/Vienna"
}
2043-08-04T09:00:00+01:00

这表明内部时间数据库缺少 2043 年的信息,就像当年一样。我们可以通过以下方式进一步确认:

$met = new DateTimeZone('Europe/Vienna');
var_dump($met->getTransitions((new DateTime('2017-01-01'))->format('U'), (new DateTime('2017-12-31'))->format('U')));
var_dump($met->getTransitions((new DateTime('2043-01-01'))->format('U'), (new DateTime('2043-12-31'))->format('U')));

这段代码(显然需要在 32 位平台上运行)打印:

array(3) {
  [0]=>
  array(5) {
    ["ts"]=>
    int(1483225200)
    ["time"]=>
    string(24) "2016-12-31T23:00:00+0000"
    ["offset"]=>
    int(3600)
    ["isdst"]=>
    bool(false)
    ["abbr"]=>
    string(3) "CET"
  }
  [1]=>
  array(5) {
    ["ts"]=>
    int(1490490000)
    ["time"]=>
    string(24) "2017-03-26T01:00:00+0000"
    ["offset"]=>
    int(7200)
    ["isdst"]=>
    bool(true)
    ["abbr"]=>
    string(4) "CEST"
  }
  [2]=>
  array(5) {
    ["ts"]=>
    int(1509238800)
    ["time"]=>
    string(24) "2017-10-29T01:00:00+0000"
    ["offset"]=>
    int(3600)
    ["isdst"]=>
    bool(false)
    ["abbr"]=>
    string(3) "CET"
  }
}
array(1) {
  [0]=>
  array(5) {
    ["ts"]=>
    int(2303679600)
    ["time"]=>
    string(24) "2042-12-31T23:00:00+0000"
    ["offset"]=>
    int(3600)
    ["isdst"]=>
    bool(false)
    ["abbr"]=>
    string(3) "CET"
  }
}

正如 PHP 所知,2043 年是从 1 月到 12 月的 CET。

此信息来自奥尔森数据库

有关世界时区的信息的协作汇编,主要用于计算机程序和操作系统

PHP 手册包括更新它的说明,以防它已经有丢失的数据。如果没有,你可能不走运。

于 2017-03-21T16:22:00.263 回答
1

显然,DateTimeZone过渡期仅限于 2037 年。这就是为什么直到那一年你才能获得正确的结果:

$met = new DateTimeZone('Europe/Vienna');

print_r($met->getTransitions());

最后的条目是:

[139] => Array
    (
        [ts] => 2108595600
        [time] => 2036-10-26T01:00:00+0000
        [offset] => 3600
        [isdst] => 
        [abbr] => CET
    )

[140] => Array
    (
        [ts] => 2121901200
        [time] => 2037-03-29T01:00:00+0000
        [offset] => 7200
        [isdst] => 1
        [abbr] => CEST
    )

[141] => Array
    (
        [ts] => 2140045200
        [time] => 2037-10-25T01:00:00+0000
        [offset] => 3600
        [isdst] => 
        [abbr] => CET
    )
于 2017-03-21T16:23:58.123 回答