Как стать автором
Обновить

Dater — определяет часовой пояс, локализует и форматирует время в PHP

Время на прочтение 4 мин
Количество просмотров 22K


Когда при разработке веб-проекта встаёт задача учёта часовых поясов пользователей, то каждый раз решение как будто приходится изобретать заново. Из множества известных мне проектов решивших эту задачу я не припомню ни одного решения, которое можно было бы назвать простым, прозрачным для всей архитектуры и универсальным.

А что если хочется просто несколько строчек кода, и чтобы весь остальной код никогда не узнал, что данные клиенту нужно отдавать в другом часовом поясе, и что в данных пришедших от клиента время может быть указано с часовым поясом отличным от серверного? Для решения этой и многих других задач, сегодня я расскажу вам про одну лаконичную и функциональную библиотеку для PHP.

И так, встречайте — Dater, и его основные возможности:

  • Биндинг форматов
  • Локализация текстов и форматов
  • Расширение списка опций форматирования
  • Автоопределение часового пояса
  • Конвертация времени с учётом часового пояса
  • Автоматическая конвертация времени в $_GET, $_POST, $_REQUEST с учётом часового пояса
  • Автоматическая конвертация часового пояса в шаблоне отправляемых данных

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

Биндинг форматов


Позволяет стандартизовать для проекта набор используемых форматов и работать с ними по имени:

$dater = new Dater\Dater(new Dater\Locale\En());
$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\En());
echo $dater->date(); // 03/21/2013
echo $dater->now('j F Y'); // 21 March 2013

$dater->setLocale(Dater\Dater::getLocaleByCode('ru'));
echo $dater->date(); // 21.03.2013
echo $dater->now('j F Y'); // 21 марта 2013

Стандартные методы для серверных и пользовательских форматов с учётом локали


echo $dater->date(); // 03/21/2013 (client timezone, depends on locale)
echo $dater->time(); // 5:41 AM (client timezone, depends on locale)
echo $dater->datetime(); // 03/21/2013 5:41 (client timezone, depends on locale)
echo $dater->isoDate(); // 2013-03-21 (client timezone)
echo $dater->isoTime(); // 05:41:28 (client timezone)
echo $dater->isoDatetime(); // 2013-03-21 05:41:28 (client timezone)
echo $dater->serverDate(); // 2013-03-21 (server timezone)
echo $dater->serverTime(); // 09:41:28 (server timezone)
echo $dater->serverDatetime(); // 2013-03-21 09:41:28 (server timezone)

Конвертация даты-времени с учётом часового пояса


$dater->setServerTimezone('Europe/Moscow');
$dater->setClientTimezone('Europe/London');
echo $dater->serverDatetime(); // 2013-03-21 08:18:06
echo $dater->isoDatetime(); // 2013-03-21 04:18:06
echo $dater->time(); // 04:18

Стоит упомянуть, что при вызове $dater->setServerTimezone('Europe/Moscow'); функция date() и класс DateTime будут возвращать время в новом установленном часовом поясе. Чтобы это отключить передайте методу false вторым параметром.

И наконец обещанное


Код, который позволит вам автоматически определить часовой пояс клиента и выводить для него актуальную дату-время:

В заголовке глобального скрипта инициализации

$dater = new Dater\Dater(new Dater\Locale\Ru(), '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 лицензией, пользуйтесь и распространяйте как пожелаете.

Надеюсь кому-нибудь таки пригодится :)

UPD По многочисленным просьбам библиотека была зарефакторена с использованием namespace, в соответствии с PSR-0. Предыдущая версия сохранена в отдельный бранч, и доступна из Composer как "dater/dater":"1.*@dev".
Теги:
Хабы:
+27
Комментарии 92
Комментарии Комментарии 92

Публикации

Истории

Работа

PHP программист
175 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн