Работа с часовыми поясами в PHP
Некоторые проблемы с часовыми поясами в PHP уже давно находятся в моем сознании, и мне было интересно, есть ли у них лучшие способы справиться с этим, чем то, что я сейчас делаю.
Все проблемы связаны с изменением даты хранения базы данных:
При работе с сайтом, который должен поддерживать несколько часовых поясов (для пользователей), чтобы нормализовать тайм-аут временной шкалы хранимых временных меток, я всегда храню его в часовом поясе сервера с помощью атрибута CURRENT_TIMESTAMP
или функции NOW()
.
Таким образом, мне не нужно учитывать, какой временной интервал был задан для PHP, когда была введена метка времени (так как функции времени PHP имеют информацию о часовом поясе). Для каждого пользователя, в соответствии с его предпочтениями, я устанавливаю часовой пояс где-то в моем загрузочном файле, используя:
date_default_timezone_set($timezone);
Когда я ищу формат даты с помощью функции php date()
, некоторая форма преобразования должна произойти, поскольку MySQL в настоящее время хранит временную метку в формате Y-m-d H:i:s
. Не обращая внимания на часовой пояс, вы можете просто запустить:
$date = date($format,strtotime($dbTimestamp));
Проблема заключается в том, что date()
и strtotime()
являются одновременно функциями, зависящими от времени, что означает, что если часовой пояс PHP задан иначе, чем часовой пояс сервера, смещение временной зоны будет применяться дважды (вместо того,).
Чтобы справиться с этим, я обычно извлекаю временные метки MySQL, используя функцию UNIX_TIMESTAMP()
, которая не имеет информации о часовом поясе, что позволяет мне применять date()
непосредственно к ней, тем самым применяя только временное смещение.
Мне не нравится этот "взломать", поскольку я больше не могу извлекать эти столбцы, как обычно, или использовать *
для извлечения всех столбцов (иногда это упрощает запросы). Кроме того, иногда это просто не вариант использования UNIX_TIMESTAMP()
(особенно при использовании с пакетами с открытым исходным кодом без значительной абстракции для композиции запроса).
Другая проблема заключается в сохранении метки времени, когда использование CURRENT_TIMESTAMP
или NOW()
не является опцией - сохранение созданной PHP метки времени сохранит ее со смещением часового пояса, которое я бы хотел избежать.
Мне, вероятно, не хватает чего-то действительно элементарного здесь, но до сих пор мне не удалось найти общее решение для решения этих проблем, поэтому я вынужден рассматривать их в каждом конкретном случае. Ваши мысли очень приветствуются
Ответы
Ответ 1
Несколько месяцев назад мы некоторое время думали об этом. Техника, с которой мы закончили, довольно проста:
- Сохранять даты в GMT/UTC (например, 0 смещение временной зоны).
- Применять текущее смещение часового пояса пользователя после извлечения из базы данных (например, перед показом пользователю или когда захотите).
Мы используем формат временных меток Unix. Но это не имеет значения.
Ответ 2
Начиная с PHP 5.2 вы можете использовать DateTime, что упрощает работу с часовыми поясами:
$datetime = new DateTime($dbTimestamp, $timezone);
echo $datetime->format('Y-m-d H:i:s');
$datetime->setTimezone(new DateTimeZone('Pacific/Nauru'));
echo $datetime->format('Y-m-d H:i:s');
Ответ 3
Вы можете попробовать заставить MySQL использовать UTC везде, используя SET time_zone
.
К сожалению, у меня нет ответа на предмет strtotime/UNIX_TIMESTAMP, на самом деле у меня такая же проблема с Postgres.
Ответ 4
Я не нашел никакого элегантного решения в сети, поэтому я создал генератор HTML-кода Timezone script, и здесь вывод напрямую. Это что-то вроде этого:
<select name="timezone" id="timezone">
<optgroup label="UTC -11:00">
<option value="Pacific/Midway">UTC -11:00 Midway</option>
<option value="Pacific/Niue">UTC -11:00 Niue</option>
<option value="Pacific/Pago_Pago">UTC -11:00 Pago_Pago</option>
</optgroup>
<optgroup label="UTC -10:00">
<option value="America/Adak">UTC -10:00 Adak</option>
<option value="Pacific/Honolulu">UTC -10:00 Honolulu</option>
<option value="Pacific/Johnston">UTC -10:00 Johnston</option>
<option value="Pacific/Rarotonga">UTC -10:00 Rarotonga</option>
<option value="Pacific/Tahiti">UTC -10:00 Tahiti</option>
</optgroup>
. . . . . . . . . . . . . .
<optgroup label="UTC +13:00">
<option value="Pacific/Apia">UTC +13:00 Apia</option>
<option value="Pacific/Enderbury">UTC +13:00 Enderbury</option>
<option value="Pacific/Fakaofo">UTC +13:00 Fakaofo</option>
<option value="Pacific/Tongatapu">UTC +13:00 Tongatapu</option>
</optgroup>
<optgroup label="UTC +14:00">
<option value="Pacific/Kiritimati">UTC +14:00 Kiritimati</option>
</optgroup>
</select>
Наслаждайтесь!