PHP DateTime setTimezone 2038

Я конвертирую все даты с 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 необходимо рассчитать +2 часа от UTC из-за «летнего времени».

Если я изменю 2043-08-04 с 08:00:00 на 2037-08-04 08:00:00, я получу правильное время.

Я знаю, что это проблема 2038 с 32-разрядным целым числом, но как я могу использовать 64-разрядное целое число с функцией DateTime setTimezone?

Спасибо

2

Решение

Если бы проблема имела какое-то отношение к переполнению 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 год — CET с января по декабрь.

Эта информация исходит от База данных Олсона:

совместный сбор информации о часовых поясах мира, в первую очередь предназначенный для использования с компьютерными программами и операционными системами

Руководство по PHP включает в себя инструкции по его обновлению, в случае, если у него уже есть недостающие данные. Если это не так, вам, вероятно, не повезло.

3

Другие решения

По-видимому 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
)
1

По вопросам рекламы [email protected]