问题
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