Работа с часовыми поясами в JavaScript требует явного разделения между локальным временем среды выполнения, универсальным временем (UTC) и региональными временными зонами, определяемыми через IANA Time Zone Database. Day.js реализует этот слой через плагины, не включая поддержку таймзон в ядро библиотеки, что делает поведение предсказуемым и минималистичным.
Ключевая особенность подхода Day.js — разделение ответственности:
Такой подход исключает неоднозначности при парсинге дат и упрощает контроль над преобразованиями.
Day.js всегда хранит внутреннее представление даты как timestamp (количество миллисекунд с Unix epoch). Разница проявляется только на этапе интерпретации и форматирования.
import dayjs from 'dayjs';
const a = dayjs(); // локальное время окружения
const b = dayjs.utc(); // UTC-время
UTC-режим фиксирует точку отсчёта без смещения относительно локальной зоны. Это фундамент для любых операций с часовыми поясами, поскольку перевод между зонами всегда сводится к преобразованию через UTC.
Работа с часовыми поясами в Day.js требует двух ключевых расширений:
utc — обеспечивает базовую работу с UTCtimezone — добавляет поддержку IANA-зонimport dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
Плагин timezone зависит от utc, поскольку
все преобразования проходят через универсальное время.
Часовые пояса в Day.js задаются строками вида:
Europe/BerlinAsia/AlmatyAmerica/New_YorkЭти идентификаторы описывают не фиксированное смещение, а набор правил:
По этой причине прямое использование числовых оффсетов (например,
+5) не эквивалентно IANA-зоне.
Метод tz позволяет интерпретировать дату в контексте
указанной зоны:
const d = dayjs.tz('2026-01-01 12:00', 'Asia/Almaty');
Здесь строка времени рассматривается как локальное время для зоны
Asia/Almaty, а не как локальное время системы.
Если зона не указана, используется системная:
const d = dayjs.tz('2026-01-01 12:00');
Любая дата в Day.js может быть переведена в другую зону через
повторное применение tz:
const base = dayjs.tz('2026-01-01 12:00', 'Europe/London');
const ny = base.tz('America/New_York');
const tokyo = base.tz('Asia/Tokyo');
При этом происходит не изменение времени как такового, а его переинтерпретация относительно новой зоны с учётом UTC-основы.
Форматирование даты может учитывать активную временную зону:
dayjs.tz('2026-01-01 12:00', 'Asia/Almaty')
.format('YYYY-MM-DD HH:mm z');
Маркер z выводит краткое обозначение зоны (если
доступно), а сам результат зависит от текущего контекста таймзоны
объекта.
Важно различать:
format() — просто вывод значенияtz() — изменение контекста зоныDay.js позволяет получать текущее время не только системы, но и конкретной зоны:
const nowAlmaty = dayjs().tz('Asia/Almaty');
const nowTokyo = dayjs().tz('Asia/Tokyo');
Каждый вызов возвращает момент времени, синхронизированный с UTC, но отображаемый в разных локальных правилах.
Оффсет — это смещение от UTC в минутах:
dayjs().utcOffset();
Однако использование только оффсета приводит к потере информации о:
Поэтому оффсеты применяются как промежуточный уровень, но не как основная модель.
Один из ключевых сценариев — нормализация входных данных:
const local = dayjs('2026-01-01 12:00');
const utcTime = local.utc();
Этот шаг критичен при сохранении дат в базе данных. UTC становится единым стандартом хранения независимо от пользовательской зоны.
После хранения UTC-значений часто требуется восстановление локального представления:
const fromDb = dayjs.utc('2026-01-01T09:00:00Z');
const inAlmaty = fromDb.tz('Asia/Almaty');
Здесь происходит наложение региональных правил отображения поверх универсального времени.
Day.js через IANA-зоны учитывает переходы на летнее время автоматически.
Пример поведения:
dayjs.tz('2026-03-29 02:30', 'Europe/Berlin');
Некоторые локальные времена могут быть невалидными из-за перехода DST, и библиотека корректирует их согласно правилам зоны.
Сравнение выполняется через UTC, а не через отображаемое локальное время:
const a = dayjs.tz('2026-01-01 10:00', 'Asia/Tokyo');
const b = dayjs.tz('2026-01-01 03:00', 'Europe/Moscow');
a.valueOf() === b.valueOf();
Несмотря на разное локальное отображение, сравнение основано на абсолютном времени.
ISO-строки с указанием зоны обрабатываются напрямую:
dayjs('2026-01-01T12:00:00+05:00');
В таких случаях Day.js извлекает смещение и переводит значение в UTC.
Если же зона задаётся отдельно:
dayjs.tz('2026-01-01T12:00:00', 'Asia/Almaty');
строка интерпретируется как локальная для указанной зоны без явного смещения.
Метод tz может использоваться в режиме “переключения
представления”:
const base = dayjs.utc('2026-01-01T12:00:00Z');
const almatyView = base.tz('Asia/Almaty');
const berlinView = base.tz('Europe/Berlin');
Здесь сохраняется один и тот же момент времени, но меняется способ его отображения.
На практике часто встречаются следующие проблемы:
tz() без понимания текущего
контекстаКаждая из этих ошибок приводит к рассинхронизации отображаемого времени между клиентом и сервером.
Стандартный подход при использовании Day.js:
tz() на стороне
клиентаconst payload = dayjs().utc().toISOString();
Такой формат исключает зависимость от локальной среды выполнения.
На сервере Node.js отсутствует пользовательская локальная зона, но системная всё равно существует. Это создаёт риск неоднородного поведения между средами.
Использование явного UTC и IANA-зон устраняет эту неоднозначность:
const serverTime = dayjs.utc();
const userTime = serverTime.tz('Asia/Almaty');
Внутренняя логика Day.js при работе с часовыми поясами сводится к следующей цепочке:
Эта модель позволяет поддерживать консистентность времени независимо от окружения и локальных настроек системы.