Работа с часовыми поясами в JavaScript строится вокруг стандарта IANA
Time Zone Database и механизмов, предоставляемых объектом
Intl. Основной инструмент —
Intl.DateTimeFormat, который позволяет форматировать даты с
учётом локали и конкретного часового пояса без ручной коррекции смещений
и без прямой работы с UTC-часами на уровне бизнес-логики.
В международной системе времени часовой пояс задаётся строковым идентификатором из базы IANA, например:
"UTC""Europe/London""Asia/Almaty""America/New_York"Эти идентификаторы описывают правила смещения относительно UTC и учитывают переходы на летнее/зимнее время.
Ключевой момент: JavaScript не оперирует числовыми смещениями как основным способом задания зоны — используется именованная зона IANA.
timeZone в Intl.DateTimeFormatОсновной способ задания часового пояса — опция
timeZone:
const formatter = new Intl.DateTimeFormat('ru-RU', {
timeZone: 'Asia/Almaty',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
formatter.format(new Date());
Если timeZone не указан, используется локальная зона
окружения выполнения (браузер или сервер).
Intl.DateTimeFormat не изменяет объект
Date. Он лишь интерпретирует его значение в контексте
указанной зоны.
const date = new Date('2026-01-01T12:00:00Z');
const f1 = new Intl.DateTimeFormat('en-US', {
timeZone: 'UTC',
timeStyle: 'short'
});
const f2 = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
timeStyle: 'short'
});
f1.format(date); // 12:00 PM
f2.format(date); // 7:00 AM (зависит от смещения)
Важно: одна и та же дата интерпретируется по-разному в разных зонах, но сама точка времени остаётся неизменной.
Современные реализации ECMAScript предоставляют метод:
Intl.supportedValuesOf('timeZone');
Он возвращает список поддерживаемых IANA-зон:
[
"Africa/Abidjan",
"Africa/Accra",
"Africa/Addis_Ababa",
...
]
Использование этого метода важно для:
Часовые пояса IANA включают исторические и политические изменения.
Например, зона может:
Это означает, что результат форматирования зависит не только от даты, но и от временного контекста.
new Intl.DateTimeFormat('en-US', {
timeZone: 'Europe/Berlin',
timeStyle: 'short',
dateStyle: 'short'
}).format(new Date('2024-07-01T12:00:00Z'));
Опция timeZoneName позволяет вывести человекочитаемое
имя зоны:
new Intl.DateTimeFormat('ru-RU', {
timeZone: 'Asia/Almaty',
timeZoneName: 'long'
}).format(new Date());
Возможные значения:
"short" — краткое обозначение (например, GMT+6)"long" — полное описание (например, «Астана, Алматы
время» в зависимости от локали)"shortOffset" / "longOffset" — смещение
относительно UTCformatToPartsДля точного контроля форматирования используется:
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Tokyo',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
formatter.formatToParts(new Date());
Результат:
[
{ type: 'month', value: '01' },
{ type: 'literal', value: '/' },
{ type: 'day', value: '24' },
...
]
Это позволяет:
resolvedOptions() и
фактическая зонаМетод resolvedOptions() показывает, какая зона реально
используется:
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Almaty'
});
formatter.resolvedOptions();
Пример результата:
{
locale: 'en-US',
timeZone: 'Asia/Almaty',
calendar: 'gregory',
numberingSystem: 'latn'
}
Этот метод полезен для:
Любая работа Intl с датами опирается на
UTC-представление Date.
new Date().toISOString();
Intl.DateTimeFormat выполняет преобразование:
UTC → IANA time zone → локальные правила → форматирование
Никаких ручных пересчётов смещения в прикладном коде не требуется.
timeZone: '+06:00' // некорректно в большинстве окружений
Правильно:
timeZone: 'Asia/Almaty'
const d = new Date();
d.setHours(d.getHours() + 6);
Такой подход ломает DST-логику и приводит к ошибкам при переходах времени.
Часовой пояс — не фиксированное число часов. Он может меняться исторически, и Intl учитывает это автоматически.
Создание Intl.DateTimeFormat — относительно дорогая
операция.
Оптимальная практика:
const formatter = new Intl.DateTimeFormat('ru-RU', {
timeZone: 'Asia/Almaty',
dateStyle: 'medium',
timeStyle: 'short'
});
// переиспользование
formatter.format(new Date());
Создание одного форматтера и его переиспользование значительно эффективнее, чем пересоздание на каждый вызов.
Один и тот же часовой пояс может отображаться по-разному в зависимости от локали:
new Intl.DateTimeFormat('en-US', { timeZone: 'Asia/Tokyo' })
new Intl.DateTimeFormat('ja-JP', { timeZone: 'Asia/Tokyo' })
Различия:
Часовые пояса часто комбинируются с:
Intl.RelativeTimeFormat — относительное времяIntl.NumberFormat — числовые единицы времениIntl.Locale — анализ локалиПример связки:
const dateFormatter = new Intl.DateTimeFormat('ru-RU', {
timeZone: 'Europe/Moscow',
dateStyle: 'full',
timeStyle: 'short'
});
В Node.js часовой пояс по умолчанию может отличаться от браузера.
process.env.TZ = 'UTC';
или через timeZone в Intl:
new Intl.DateTimeFormat('en-US', {
timeZone: 'UTC'
});
Это устраняет зависимость от системных настроек сервера.
При проектировании систем с часовыми поясами используется единая схема:
Date или ISO-строкиIntl.DateTimeFormatТакой подход исключает накопление ошибок смещения и обеспечивает предсказуемое поведение при смене зон и переходах на летнее время.