Сегодня столкнулась с интересной функцией (? DateTime::setTimestamp()
поведении PHP DateTime::setTimestamp()
. Во время зимнего изменения DST, когда 1 час повторяется дважды, PHP преобразует временную метку, чтобы всегда быть вторым часом. Рассмотрим следующий пример:
<?php $date = new \DateTime("now", new \DateTimeZone("UTC")); //2018-10-28T01:30:00 UTC, in London DST happens $date->setTimestamp(1540686600); echo $date->getTimestamp() . "\n"; //1540686600 echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00 $date->setTimezone(new \DateTimeZone("Europe/London")); echo $date->getTimestamp() . "\n"; //1540690200 $date->setTimezone(new \DateTimeZone("UTC")); echo $date->getTimestamp() . "\n"; //1540690200 echo $date->format('c') . "\n"; //2018-10-28T01:30:00+00:00
-<?php $date = new \DateTime("now", new \DateTimeZone("UTC")); //2018-10-28T01:30:00 UTC, in London DST happens $date->setTimestamp(1540686600); echo $date->getTimestamp() . "\n"; //1540686600 echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00 $date->setTimezone(new \DateTimeZone("Europe/London")); echo $date->getTimestamp() . "\n"; //1540690200 $date->setTimezone(new \DateTimeZone("UTC")); echo $date->getTimestamp() . "\n"; //1540690200 echo $date->format('c') . "\n"; //2018-10-28T01:30:00+00:00
Глядя на источник PHP пытается преобразовать временную метку в локальное время (o_O). Итак, два вопроса:
DateTime
? (Я знаю о возможности сохранить все в UTC
и конвертировать в местный часовой пояс только при показе) Найдена возможная ошибка, зарегистрированная на PHP
UPD 2016-01-04 (в UTC) :
Суженная проблема для DateTime::getTimestamp()
. Рассмотрим следующий код:
<?php $date = new \DateTime("now", new \DateTimeZone("UTC")); //2018-10-28T00:30:00 UTC $date->setTimestamp(1540686600); echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00 $date->setTimezone(new \DateTimeZone("Europe/London")); echo $date->format('c') . "\n"; //2018-10-28T01:30:00+01:00 $date->setTimezone(new \DateTimeZone("UTC")); echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00 echo $date->getTimestamp() . "\n"; //1540686600
-<?php $date = new \DateTime("now", new \DateTimeZone("UTC")); //2018-10-28T00:30:00 UTC $date->setTimestamp(1540686600); echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00 $date->setTimezone(new \DateTimeZone("Europe/London")); echo $date->format('c') . "\n"; //2018-10-28T01:30:00+01:00 $date->setTimezone(new \DateTimeZone("UTC")); echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00 echo $date->getTimestamp() . "\n"; //1540686600
Здесь timestamp не изменяется, и код работает так, как ожидалось. Следующий код, похожий и приведенный в оригинальном примере, нарушен:
<?php $date = new \DateTime("now", new \DateTimeZone("UTC")); //2018-10-28T00:30:00 UTC $date->setTimestamp(1540686600); echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00 $date->setTimezone(new \DateTimeZone("Europe/London")); echo $date->format('c') . "\n"; //2018-10-28T01:30:00+01:00 //-------------- the only line that was added ------------------ echo $date->getTimestamp() . "\n"; //1540690200 //-------------- end of added line ----------------------------- $date->setTimezone(new \DateTimeZone("UTC")); echo $date->format('c') . "\n"; //2018-10-28T01:30:00+00:00 echo $date->getTimestamp() . "\n"; //1540690200
-<?php $date = new \DateTime("now", new \DateTimeZone("UTC")); //2018-10-28T00:30:00 UTC $date->setTimestamp(1540686600); echo $date->format('c') . "\n"; //2018-10-28T00:30:00+00:00 $date->setTimezone(new \DateTimeZone("Europe/London")); echo $date->format('c') . "\n"; //2018-10-28T01:30:00+01:00 //-------------- the only line that was added ------------------ echo $date->getTimestamp() . "\n"; //1540690200 //-------------- end of added line ----------------------------- $date->setTimezone(new \DateTimeZone("UTC")); echo $date->format('c') . "\n"; //2018-10-28T01:30:00+00:00 echo $date->getTimestamp() . "\n"; //1540690200
Ты сказал:
Временная метка всегда находится в UTC и должна быть местным временем, агрегированным часовым поясом
Это утверждение противоречит самому себе. Временные метки находятся в UTC, да, но это не локальное время или часовая зона.
Другими словами, временная метка всегда относится к определенному моменту времени. Местное время может быть неоднозначным, но временной отметки нет. Если вам нужен первый экземпляр неоднозначного локального времени, укажите метку времени, которая на час раньше.
Однако ваши данные немного неверны. Указанная вами временная метка, 1540686600
, на самом деле не 1:30 UTC, а соответствует 2018-10-28T01:30:00+01:00
. Таким образом, это действительно первое появление в 1:30 в день переходного летнего перехода на летнее время. Второе возникновение будет 2018-10-28T01:30:00+00:00
, что соответствует 1540690200
.
Что касается вашего второго вопроса:
есть ли какое-либо простое решение сохранить идентификатор часового пояса и предполагаемую метку времени в объекте DateTime?
Вот как работает DateTime
в PHP. Он состоит из внутренней метки времени и связанного часового пояса. Это именно то, что вы показываете при вызове setTimezone
.