PHP DateTime setTimezone 2038

妖精的绣舞 提交于 2020-01-02 15:02:44

问题


I convert all dates with DateTime (from UTC to Europe/Vienna) in my project. Now I have dates with above 2038, and cannot get the correct time.

Example Code:

$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

Between 2043-03-29 and 2043-10-25 there is to calculate +2 hours from UTC, because of "summertime".

If I change 2043-08-04 08:00:00 to 2037-08-04 08:00:00 I get the correct time.

I know it is a 2038 problem with integer 32 bit, but how I can use integer 64 bit with DateTime setTimezone function?

Thanks


回答1:


If the issue had anything to do with overflowing 32-bit Unix timestamps you'd be getting dates around 1970 and similar totally incorrect results. However, your result is only off by one hour. In fact, we could argue that the hour is correct since you're only getting a wrong time zone offset, as you can see if you print time zone information:

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

This suggests that the internal time database is missing information for year 2043 as it has for current year. We can further confirm this with:

$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')));

This code (that apparently needs to run on a 32-bit platforms) prints:

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"
  }
}

As PHP is aware of it, year 2043 is CET from January to December.

This information comes from the Olson database:

a collaborative compilation of information about the world's time zones, primarily intended for use with computer programs and operating systems

The PHP manual includes instructions to update it, in case it already has the missing data. If it doesn't, you're probably out of luck.




回答2:


Aparrently the DateTimeZone transitions are limited to 2037. That's why you are getting the right results until that year:

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

print_r($met->getTransitions());

The last entries are:

[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
    )


来源:https://stackoverflow.com/questions/42931978/php-datetime-settimezone-2038

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!