Локализация времени в JavaScript
Давайте представим, что у вас есть определенное время, которое вы хотели бы отобразить на своем сайте. Время имеет формат вашего часового пояса. Конечно, вы можете показывать его с явным указанием часового пояса, скажем, так: 3:00 PM Eastern Standard Time. В этом случае каждый посетитель вашего сайта будет вынужден вручную сконвертировать время с поправкой на свой часовой пояс, ну или использовать этот отличный ресурс — Every Time Zone.
Но ведь разработчик может позаботиться о пользователе и локализовать время в соответствии с его часовым поясом, избавив от необходимости делать это самостоятельно. Тем более, что у нас есть отличный инструмент — JavaScript.
Уверен, для решения этой проблемы существует множество способов. Недавно мне пришлось столкнуться с подобной задачей на одном новом проекте, и я решил перенять опыт локализации дат у ребят из ShopTalk.
Библиотеки
Moment.js и Moment Timezone прекрасно подходят для решения задач, связанных с локализацией дат и различными манипуляциями с ними. Например, с помощью этих библиотек можно легко представить время в подобных форматах: “32 minutes ago”, “12 de Agosto de 2015”.
Ещё один важный момент заключается в том, что было бы неплохо автоматически определять текущий часовой пояс пользователя. К счастью, для этого есть отличное средство.
Итак, мы будем использовать:
- jstz.js
- moment.js
- moment-timezone.js
Шаг 1: Получение часового пояса
После получения часового пояса можно хранить его в localStorage
, чтобы было проще получить его из любой точки приложения:
if (!sessionStorage.getItem('timezone')) {
var tz = jstz.determine() || 'UTC';
sessionStorage.setItem('timezone', tz.name());
}
var currTz = sessionStorage.getItem('timezone');
Шаг 2: Получение времени
Когда мы создаем объект Moment, сначала выполняется проверка переданной в качестве аргумента строки на соответствие формату ISO 8601. Для UTC этот формат имеет следующий вид (обратите внимание на символ ‘Z’, стоящий сразу же после значения времени):
2015-08-12T14:30Z
Используя Moment можно легко изменять формат дат. Давайте рассмотрим небольшой пример. По умолчанию Moment создаёт объект, основанный на текущей дате, который мы преобразуем в строку нужного формата:
var date = moment().format("YYYY-MM-DD");
Затем, если необходимо, добавим к получившейся строке время в формате TH:mm
:
var stamp = date + "T" + theTime + "Z";
И, наконец, преобразуем строку в новый объект Moment:
var momentTime = moment(stamp);
Шаг 3: Локализация времени
Теперь давайте посмотрим, как можно использовать Moment Timezone для локализации времени:
var tzTime = momentTime.tz(currTz);
И, конечно же, отформатируем получившееся значение для более удобного отображения:
var formattedTime = tzTime.format('h:mm A');
Шаг 4: Использование
Ну что же, теперь мы можем использовать описанный выше подход на любом сайте. Вместе с отформатированным временем вы можете отображать и текущий часовой пояс. Если же определение часового пояса по каким-то причинам завершилось неудачей, то время отобразится в UTC формате:
output.textContent = "Time in " + currTz + ": " + formattedTime;
Дополнительная настройка
Иногда бывает полезно предоставлять пользователям возможность вводить даты на сайте в их локальном часовом поясе, но хранить подобную информацию удобнее в формате UTC. Moment может достаточно легко это сделать с помощью методов, которые позволяют выполнять почасовой сдвиг:
moment().subtract(4, 'hours');
moment().add(3, 'hours');
Мы можем выполнить подобные манипуляции и на сервере, используя, например, PHP:
<?php $sixHoursAgo = strtotime("-6 hours", time()); ?>
хотя в подобных преобразованиях лучше использовать Moment Timezone, чтобы избежать неточностей, связанных с переводом времени на летнее.
Пример
В примере ниже мы будем использовать HTML5 time editor и на лету конвертировать время в локальное:
Посмотреть на CodePen: Приведение времени в соответствие локальному часовому поясу.
Другие способы локализации
У вас есть опыт решения задач локализации времени? В статье мы использовали довольно объёмные сторонние библиотеки, хотя в JavaScript уже есть нативные методы (например, getTimezoneOffset()), с помощью которых можно добиться аналогичных результатов. Было бы интересно увидеть ваши решения, напишите о них в комментариях.