Локализация времени в JavaScript

Давайте представим, что у вас есть определенное время, которое вы хотели бы отобразить на своем сайте. Время имеет формат вашего часового пояса. Конечно, вы можете показывать его с явным указанием часового пояса, скажем, так: 3:00 PM Eastern Standard Time. В этом случае каждый посетитель вашего сайта будет вынужден вручную сконвертировать время с поправкой на свой часовой пояс, ну или использовать этот отличный ресурс — Every Time Zone.

Но ведь разработчик может позаботиться о пользователе и локализовать время в соответствии с его часовым поясом, избавив от необходимости делать это самостоятельно. Тем более, что у нас есть отличный инструмент — JavaScript.

Уверен, для решения этой проблемы существует множество способов. Недавно мне пришлось столкнуться с подобной задачей на одном новом проекте, и я решил перенять опыт локализации дат у ребят из ShopTalk.

Библиотеки

Moment.js и Moment Timezone прекрасно подходят для решения задач, связанных с локализацией дат и различными манипуляциями с ними. Например, с помощью этих библиотек можно легко представить время в подобных форматах: “32 minutes ago”, “12 de Agosto de 2015”.

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

Итак, мы будем использовать:

Шаг 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()), с помощью которых можно добиться аналогичных результатов. Было бы интересно увидеть ваши решения, напишите о них в комментариях.