Конвертация между зонами

Day.js изначально работает с датами в локальной среде выполнения JavaScript. Это означает, что без дополнительных плагинов библиотека не выполняет полноценную конвертацию между временными зонами, а лишь оперирует объектом времени в текущем окружении (браузер или Node.js).

Для работы с часовыми поясами используется комбинация плагинов:

  • utc — переводит Day.js в универсальный координированный формат времени (UTC)
  • timezone — добавляет поддержку IANA-зон (например, Europe/Berlin, Asia/Almaty)
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'

dayjs.extend(utc)
dayjs.extend(timezone)

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


Понимание UTC как основы конвертации

Любая работа с часовыми поясами в Day.js опирается на концепцию UTC как нейтральной базы.

const nowUtc = dayjs().utc()
console.log(nowUtc.format())

Ключевой момент:

  • utc() не «переводит время», а интерпретирует текущий момент как UTC
  • все последующие преобразования выполняются относительно этой точки

Это важно для избежания ошибок при работе с локальными датами, особенно в серверных приложениях.


Перевод локального времени в UTC

При обработке пользовательского ввода часто возникает необходимость нормализовать дату в UTC.

const local = dayjs('2026-05-23 12:00')
const utcTime = local.utc()

console.log(utcTime.format())

Здесь происходит не изменение момента времени, а смена представления:

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

Типичная ошибка — считать, что utc() просто «меняет метку». На самом деле происходит пересчёт временной оси.


Работа с IANA временными зонами

Плагин timezone добавляет поддержку стандартизированных зон:

const moscow = dayjs().tz('Europe/Moscow')
const tokyo = dayjs().tz('Asia/Tokyo')

console.log(moscow.format())
console.log(tokyo.format())

Особенности:

  • зоны задаются по базе IANA
  • учитываются переходы на летнее/зимнее время
  • корректно обрабатываются исторические изменения правил зон

Конвертация времени между зонами

Прямое преобразование

Основной сценарий — перевод одного и того же момента в разные зоны:

const base = dayjs.utc('2026-05-23T10:00:00Z')

const newYork = base.tz('America/New_York')
const london = base.tz('Europe/London')

console.log(newYork.format())
console.log(london.format())

Важно:

  • исходная точка всегда фиксируется в UTC
  • tz() меняет только отображение, не сам момент времени

Перевод локального времени в другую зону

Иногда требуется интерпретировать локальную дату как принадлежащую конкретной зоне:

const input = dayjs('2026-05-23 10:00')

const asTokyo = input.tz('Asia/Tokyo', true)

Второй аргумент true означает:

  • «не пересчитывать момент»
  • «считать, что это уже время указанной зоны»

Это критично при обработке данных из форм или API.


Форматирование с учётом временной зоны

Отображение даты зависит от выбранной зоны:

const time = dayjs.utc('2026-05-23T10:00:00Z')

console.log(time.tz('Asia/Almaty').format('YYYY-MM-DD HH:mm'))
console.log(time.tz('America/Los_Angeles').format('YYYY-MM-DD HH:mm'))

Форматирование происходит после конвертации, поэтому:

  • один и тот же момент может выглядеть по-разному
  • смещение автоматически учитывается

Получение смещения зоны

Иногда требуется вычислить разницу относительно UTC:

const offset = dayjs().tz('Europe/Moscow').utcOffset()

console.log(offset)

Особенности:

  • значение возвращается в минутах
  • положительное или отрицательное относительно UTC
  • учитывает сезонные изменения

Переключение между зонами без потери момента

Корректный паттерн конвертации:

const utcMoment = dayjs.utc('2026-05-23T10:00:00Z')

const zones = [
  'Asia/Almaty',
  'Europe/Berlin',
  'America/New_York'
]

const converted = zones.map(zone => ({
  zone,
  time: utcMoment.tz(zone).format()
}))

Принцип:

  • исходная точка всегда UTC
  • каждая зона строится от одного источника
  • исключается накопление ошибок преобразования

Частые ошибки при конвертации

Потеря UTC-контекста

// некорректно
const wrong = dayjs('2026-05-23T10:00:00Z').tz('Asia/Tokyo')

Проблема:

  • строка уже содержит UTC
  • повторная интерпретация может привести к сдвигу

Двойное применение локального времени

const a = dayjs('2026-05-23 10:00')
const b = a.tz('Europe/London').tz('Asia/Tokyo')

Ошибка:

  • второе преобразование работает уже с изменённым представлением
  • происходит накопление смещений

Игнорирование DST

Некоторые зоны изменяют смещение в течение года:

  • Europe/Berlin
  • America/New_York

Day.js корректно учитывает это, но только при использовании IANA-зон:

dayjs.tz('2026-07-01', 'Europe/Berlin')
dayjs.tz('2026-01-01', 'Europe/Berlin')

Конвертация пользовательского ввода

Типичный сценарий серверной обработки:

const userInput = '2026-05-23 18:00'
const userZone = 'Asia/Almaty'

const normalized = dayjs.tz(userInput, userZone).utc()

Результат:

  • вход интерпретируется в зоне пользователя
  • затем переводится в UTC
  • сохраняется единая временная база

Конвертация для отображения в интерфейсе

const storedUtc = dayjs.utc('2026-05-23T12:00:00Z')

function render(zone) {
  return storedUtc.tz(zone).format('HH:mm')
}

render('Asia/Almaty')
render('America/New_York')

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

  • единое хранилище времени
  • множественные представления без дублирования данных

Работа с массивами дат и массовая конвертация

const events = [
  '2026-05-23T10:00:00Z',
  '2026-05-23T12:00:00Z',
  '2026-05-23T15:00:00Z'
]

const converted = events.map(e =>
  dayjs.utc(e).tz('Asia/Almaty').format()
)

Принцип:

  • сначала нормализация в UTC
  • затем единое преобразование
  • затем форматирование

Сравнение временных зон

const a = dayjs.tz('2026-05-23T10:00:00Z', 'Asia/Almaty')
const b = dayjs.tz('2026-05-23T10:00:00Z', 'Europe/London')

console.log(a.valueOf() === b.valueOf())

Важно:

  • сравнение всегда должно выполняться в UTC-таймстампах
  • визуальное представление не должно использоваться для логики

Хранение временных данных в приложениях

Рекомендуемая схема:

  • хранение: UTC (.toISOString())
  • ввод: локальная зона пользователя
  • вывод: конвертация в нужную зону
  • логика: только UTC timestamp
const stored = dayjs().utc().toISOString()

const restored = dayjs.utc(stored)

Такой подход исключает неоднозначность и ошибки при миграции между зонами.