Когда при разработке веб-проекта встаёт задача учёта часовых поясов пользователей, то каждый раз решение как будто приходится изобретать заново. Из множества известных мне проектов решивших эту задачу я не припомню ни одного решения, которое можно было бы назвать простым, прозрачным для всей архитектуры и универсальным.
А что если хочется просто несколько строчек кода, и чтобы весь остальной код никогда не узнал, что данные клиенту нужно отдавать в другом часовом поясе, и что в данных пришедших от клиента время может быть указано с часовым поясом отличным от серверного? Для решения этой и многих других задач, сегодня я расскажу вам про одну лаконичную и функциональную библиотеку для PHP.
И так, встречайте — Dater, и его основные возможности:
- Биндинг форматов
- Локализация текстов и форматов
- Расширение списка опций форматирования
- Автоопределение часового пояса
- Конвертация времени с учётом часового пояса
- Автоматическая конвертация времени в $_GET, $_POST, $_REQUEST с учётом часового пояса
- Автоматическая конвертация часового пояса в шаблоне отправляемых данных
Обещанные строчки кода по автоматическому определению и обработке пользовательского часового пояса оставим на десерт, а пока пробежимся по основным возможностям Dater, в кратце, на примерах.
Биндинг форматов
Позволяет стандартизовать для проекта набор используемых форматов и работать с ними по имени:
$dater->format(time(), 'd/m/Y'); // 2013/03/14
$dater->addFormat('slashedDate', 'd/m/Y');
$dater->format(time(), 'slashedDate'); // 2013/03/14
$dater->slashedDate(time()); // 2013/03/14
Расширение опций форматирования
Доступны все опции форматирования из date(), которые также могут быть переопределены и расширены:
$dater->addFormatOption('ago', function (DateTime $dateTime) {
return floor((time() - $dateTime->getTimestamp()) / 86400) . ' days ago';
});
$dater->format(time() - 60*60*24*7, 'd F Y, ago'); // 14 March 2013, 7 days ago
Поддержка локалей
$dater->setLocale(new Dater_Locale_English());
echo $dater->date(); // 03/21/2013
echo $dater->now('j F Y'); // 21 March 2013
$dater->setLocale(new Dater_Locale_Russian());
echo $dater->date(); // 21.03.2013
echo $dater->now('j F Y'); // 21 марта 2013
Стандартные методы для серверных и пользовательских форматов с учётом локали
echo $dater->date(); // 03/21/2013 (depends on locale)
echo $dater->time(); // 5:41 AM (depends on locale)
echo $dater->datetime(); // 03/21/2013 5:41 (depends on locale)
echo $dater->serverDate(); // 2013-03-21
echo $dater->serverTime(); // 09:41:28
echo $dater->serverDateTime(); // 2013-03-21 09:41:28
echo $dater->clientDate(); // 2013-03-21
echo $dater->clientTime(); // 05:41:28
echo $dater->clientDateTime(); // 2013-03-21 05:41:28
Конвертация даты-времени с учётом часового пояса
$dater->setServerTimezone('Europe/Moscow');
$dater->setClientTimezone('Europe/London');
echo $dater->serverDateTime(); // 2013-03-21 08:18:06
echo $dater->clientDateTime(); // 2013-03-21 04:18:06
echo $dater->time(); // 04:18
Стоит упомянуть, что при вызове $dater->setServerTimezone('Europe/Moscow');
функция date() и класс DateTime будут возвращать время в новом установленном часовом поясе. Чтобы это отключить передайте методу false вторым параметром.
И наконец обещанное
Код, который позволит вам автоматически определить часовой пояс клиента и выводить для него актуальную дату-время:
В заголовке глобального скрипта инициализации
$dater = new Dater(new Dater_Locale_Russian(), 'Europe/Moscow');
$timezoneDetector = new Dater_TimezoneDetector();
$dater->setClientTimezone($timezoneDetector->getClientTimezone())
$dataHandler = new Dater_DataHandler($dater);
$dataHandler->enableOutputTimezoneHandler();
$dataHandler->convertRequestDataToServerTimezone();
В основном шаблоне
<html>
<head>
<?= $timezoneDetector->getHtmlJsCode() ?>
</head>
</html>
Теперь все строки YYYY-MM-DD HH:MM:SS в отправляемых данных будут заменены на YYYY-MM-DD HH:MM:SS в автоматически определённом часовом поясе клиента. Если же вам нужно выводить дату-время в определённом формате, то достаточно добавить YYYY-MM-DD HH:MM:SS[Н m d] или YYYY-MM-DD HH:MM:SS[date] где date — забинденный в Dater формат. Также можно выводить и форматировать timestamp формат: 1363853607[d.m.Y].
Например, следующие данные:
<html>
<body>
Timestamp format: 1363238564 (не изменится)
Timestamp format: 1363238564[Y/m/d]
Timestamp format: 1363238564[datetime]
Server datetime format: 2013-03-14 09:22:44[Y/m/d]
Server datetime format: 2013-03-14 09:22:44[time]
Server datetime format: 2013-03-14 09:22:44
</body>
</html>
Будут автоматически конвертированы в:
<html>
<body>
Timestamp format: 1363238564 (не изменится)
Timestamp format: 2013/03/14
Timestamp format: 14.03.2013 07:22
Server datetime format: 2013/03/14
Server datetime format: 07:22
Server datetime format: 2013-03-14 07:22:44
</body>
</html>
В то же время $dataHandler->convertRequestDataToServerTimezone();
сделает так, что все YYYY-MM-DD HH:MM:SS данные поступающие от клиента будут конвертированы в YYYY-MM-DD HH:MM:SS часового пояса сервера. Таким образом сервер никогда не узнает о том, что клиента работает получает и отправляет дату-время в другом часовом поясе.
Стоит признать, что это немножко экстримальный вариант обработки часовых поясов. Более универсальным и традиционным решением было бы отказаться от использования $dataHandler->enableOutputTimezoneHandler();
и просто обрамлять вставку каждой даты-времени вызовом соответствующего метода форматирования. Например <?= $dater->date($dateTimeOrTimestamp) ?>
.
О проекте
Честно признаюсь, что являюсь автором этой библиотеки, и буду очень благодарен за любую критику и помощь в доработке. Исходники выложены на GitHub под свободной BSD лицензией, пользуйтесь и распространяйте как пожелаете.
Надеюсь кому-нибудь таки пригодится :)
Автор: liaren