Часовые пояса

Локализация даты и времени включает не только форматирование строк, но и корректную обработку часовых поясов. Разные страны и регионы используют собственные временные зоны, правила перехода на летнее время и локальные стандарты отображения времени. Библиотека Globalize позволяет строить приложения, которые учитывают эти особенности на основе данных CLDR и возможностей Intl API.

Основы работы со временем и часовыми поясами

В JavaScript объект Date хранит время в UTC, а отображает его в локальном часовом поясе среды выполнения. Это приводит к нескольким распространённым проблемам:

  • различие времени у пользователей из разных стран;
  • некорректное отображение серверного времени;
  • ошибки при переходе на летнее время;
  • несоответствие локальных форматов отображения.

Globalize решает задачу локализованного форматирования времени, опираясь на:

  • данные CLDR;
  • модуль Globalize.dateFormatter;
  • поддержку временных зон через Intl.DateTimeFormat.

Подключение CLDR-данных

Для работы с датами и часовыми поясами требуется подключение соответствующих наборов данных.

const Globalize = require("globalize");
const cldrData = require("cldr-data");

Globalize.load(
    cldrData.entireSupplemental(),
    cldrData.entireMainFor("en", "ru")
);

Globalize.locale("ru");

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

Форматирование времени в локальной временной зоне

Простейший пример отображения времени:

const formatter = Globalize.dateFormatter({
    datetime: "medium"
});

console.log(formatter(new Date()));

Результат для русской локали:

27 мая 2026 г., 14:30:12

Для английской локали:

Globalize.locale("en");

console.log(formatter(new Date()));

Результат:

May 27, 2026, 2:30:12 PM

Формат автоматически адаптируется под культуру пользователя.

Использование временной зоны через Intl

Globalize не реализует собственный движок временных зон. Вместо этого библиотека использует возможности Intl.DateTimeFormat.

Пример форматирования времени для конкретного часового пояса:

const date = new Date();

const formatter = new Intl.DateTimeFormat("ru-RU", {
    timeZone: "Europe/Moscow",
    dateStyle: "full",
    timeStyle: "long"
});

console.log(formatter.format(date));

Результат:

среда, 27 мая 2026 г. в 14:30:12 GMT+3

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

Временные зоны задаются в формате IANA Time Zone Database.

Примеры

Регион Идентификатор
Москва Europe/Moscow
Алматы Asia/Almaty
Нью-Йорк America/New_York
Лондон Europe/London
Токио Asia/Tokyo

Использование неверного идентификатора приводит к ошибке:

new Intl.DateTimeFormat("ru-RU", {
    timeZone: "Moscow"
});

Ошибка:

RangeError: Invalid time zone specified

Комбинирование Globalize и Intl

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

  • Globalize отвечает за локаль и шаблоны форматирования;
  • Intl отвечает за временные зоны.

Пример:

Globalize.locale("ru");

const date = new Date();

const formatter = new Intl.DateTimeFormat(
    Globalize.locale().attributes.language,
    {
        timeZone: "Asia/Almaty",
        weekday: "long",
        year: "numeric",
        month: "long",
        day: "numeric",
        hour: "2-digit",
        minute: "2-digit"
    }
);

console.log(formatter.format(date));

Результат:

среда, 27 мая 2026 г., 16:30

Получение текущего часового пояса пользователя

Определение временной зоны производится через Intl.DateTimeFormat.

const timezone =
    Intl.DateTimeFormat().resolvedOptions().timeZone;

console.log(timezone);

Пример результата:

Asia/Almaty

Это особенно важно для:

  • календарей;
  • систем бронирования;
  • видеоконференций;
  • корпоративных приложений;
  • логирования событий.

Отображение времени для разных пользователей

Сервер обычно хранит время в UTC.

Пример UTC-даты:

const utcDate = new Date("2026-05-27T10:00:00Z");

Преобразование для Москвы:

const moscowFormatter = new Intl.DateTimeFormat("ru-RU", {
    timeZone: "Europe/Moscow",
    timeStyle: "short"
});

console.log(moscowFormatter.format(utcDate));

Результат:

13:00

Преобразование для Нью-Йорка:

const nyFormatter = new Intl.DateTimeFormat("en-US", {
    timeZone: "America/New_York",
    timeStyle: "short"
});

console.log(nyFormatter.format(utcDate));

Результат:

6:00 AM

Работа с UTC

UTC используется как универсальная временная основа.

Создание UTC-времени:

const date = new Date(Date.UTC(
    2026,
    4,
    27,
    10,
    0,
    0
));

Форматирование:

const formatter = new Intl.DateTimeFormat("ru-RU", {
    timeZone: "UTC",
    dateStyle: "full",
    timeStyle: "long"
});

console.log(formatter.format(date));

Результат:

среда, 27 мая 2026 г. в 10:00:00 UTC

Переход на летнее время

Многие страны используют DST (Daylight Saving Time). Intl автоматически учитывает эти изменения.

Пример:

const winter = new Date("2026-01-15T12:00:00Z");
const summer = new Date("2026-07-15T12:00:00Z");

const formatter = new Intl.DateTimeFormat("en-US", {
    timeZone: "America/New_York",
    timeZoneName: "short"
});

console.log(formatter.format(winter));
console.log(formatter.format(summer));

Результат:

1/15/2026, EST
7/15/2026, EDT

Смещение меняется автоматически.

Форматирование названия временной зоны

Параметр timeZoneName управляет отображением зоны.

short

const formatter = new Intl.DateTimeFormat("ru-RU", {
    timeZone: "Europe/Moscow",
    timeZoneName: "short"
});

Результат:

GMT+3

long

const formatter = new Intl.DateTimeFormat("ru-RU", {
    timeZone: "Europe/Moscow",
    timeZoneName: "long"
});

Результат:

Москва, стандартное время

Форматирование только времени

const formatter = new Intl.DateTimeFormat("ru-RU", {
    timeZone: "Asia/Tokyo",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit"
});

console.log(formatter.format(new Date()));

Результат:

22:15:48

Форматирование только даты

const formatter = new Intl.DateTimeFormat("ru-RU", {
    timeZone: "Europe/London",
    year: "numeric",
    month: "long",
    day: "numeric"
});

console.log(formatter.format(new Date()));

Результат:

27 мая 2026 г.

Использование шаблонов Globalize

Globalize поддерживает CLDR-шаблоны форматирования.

const formatter = Globalize.dateFormatter({
    skeleton: "GyMMMd"
});

console.log(formatter(new Date()));

Результат:

27 мая 2026 г.

Часто используемые skeleton-шаблоны

Шаблон Назначение
Hm часы и минуты
Hms часы, минуты, секунды
yMd дата
yMMMd дата с названием месяца
GyMMMd дата с эрой

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

JavaScript не изменяет сам объект Date при смене зоны. Меняется только представление.

Пример:

const date = new Date("2026-05-27T10:00:00Z");

const zones = [
    "Europe/Moscow",
    "Asia/Tokyo",
    "America/New_York"
];

zones.forEach(zone => {
    const formatter = new Intl.DateTimeFormat("en-US", {
        timeZone: zone,
        timeStyle: "long"
    });

    console.log(zone, formatter.format(date));
});

Результат:

Europe/Moscow 1:00:00 PM GMT+3
Asia/Tokyo 7:00:00 PM GMT+9
America/New_York 6:00:00 AM EDT

Ошибки при работе с часовыми поясами

Хранение локального времени вместо UTC

Неправильно:

const localDate = new Date();
database.save(localDate);

Правильно:

database.save(new Date().toISOString());

Ручной расчёт смещений

Неправильно:

const hours = utcHours + 3;

Проблемы:

  • DST;
  • исторические изменения зон;
  • региональные правила.

Правильный подход:

new Intl.DateTimeFormat("ru-RU", {
    timeZone: "Europe/Moscow"
});

Использование сокращений вместо IANA

Неправильно:

timeZone: "MSK"

Правильно:

timeZone: "Europe/Moscow"

Поддержка браузеров

Globalize и Intl поддерживаются:

Среда Поддержка
Chrome Полная
Firefox Полная
Safari Полная
Edge Полная
Node.js Полная при наличии ICU

ICU и Node.js

Для корректной интернационализации Node.js должен содержать ICU-данные.

Проверка поддержки:

console.log(
    Intl.DateTimeFormat.supportedLocalesOf(["ru"])
);

Если массив пустой, ICU отсутствует или подключён частично.

Использование с Moment Timezone

Хотя Globalize ориентирован на Intl, иногда применяется связка с Moment Timezone.

Установка:

npm install moment moment-timezone

Пример:

const moment = require("moment-timezone");

const date = moment.utc("2026-05-27T10:00:00Z");

console.log(
    date.tz("Asia/Almaty").format("HH:mm")
);

Результат:

16:00

После вычислений строка может дополнительно локализоваться через Globalize.

Использование с Luxon

Luxon считается более современным решением.

Установка:

npm install luxon

Пример:

const { DateTime } = require("luxon");

const dt = DateTime
    .utc()
    .setZone("Europe/Moscow");

console.log(
    dt.toLocaleString(DateTime.DATETIME_FULL)
);

Luxon хорошо сочетается с концепцией Intl и CLDR.

Архитектура хранения времени в приложениях

Типичная схема:

  1. Сервер хранит UTC.
  2. Клиент определяет часовую зону пользователя.
  3. Intl преобразует время.
  4. Globalize локализует формат отображения.

Пример:

const utcDate = "2026-05-27T10:00:00Z";

const userZone =
    Intl.DateTimeFormat()
        .resolvedOptions()
        .timeZone;

const formatter = new Intl.DateTimeFormat("ru-RU", {
    timeZone: userZone,
    dateStyle: "full",
    timeStyle: "short"
});

console.log(
    formatter.format(new Date(utcDate))
);

Производительность форматирования

Создание форматтера — относительно дорогая операция.

Неправильно:

for (const item of items) {
    const formatter = new Intl.DateTimeFormat("ru-RU");
    formatter.format(item.date);
}

Правильно:

const formatter =
    new Intl.DateTimeFormat("ru-RU");

for (const item of items) {
    formatter.format(item.date);
}

Кэширование форматтеров особенно важно:

  • в таблицах;
  • календарях;
  • аналитических панелях;
  • системах логирования;
  • графиках активности.

Практический пример локализованного расписания

Globalize.locale("ru");

const meetings = [
    {
        title: "Daily Meeting",
        date: "2026-05-27T09:00:00Z"
    },
    {
        title: "Deploy",
        date: "2026-05-27T15:00:00Z"
    }
];

const timezone =
    Intl.DateTimeFormat()
        .resolvedOptions()
        .timeZone;

const formatter = new Intl.DateTimeFormat("ru-RU", {
    timeZone: timezone,
    dateStyle: "short",
    timeStyle: "short"
});

meetings.forEach(meeting => {
    console.log(
        meeting.title,
        formatter.format(new Date(meeting.date))
    );
});

Такой подход обеспечивает:

  • корректное отображение времени для любого региона;
  • автоматическую поддержку DST;
  • локализацию форматов;
  • единое хранение времени на сервере.