date-fns-tz для часовых поясов

Библиотека date-fns-tz расширяет функциональность date-fns, добавляя корректную работу с часовыми поясами на основе IANA Time Zone Database. Основная задача инструмента — устранение типичных проблем JavaScript-даты, связанных с тем, что стандартный объект Date всегда хранит время в UTC, но отображает его в локальной системной зоне.

Ключевая особенность подхода — разделение понятий:

  • UTC-время (универсальное, неизменяемое)
  • локальное время пользователя
  • время в произвольной IANA-зоне

Архитектура работы с временными зонами

date-fns-tz не изменяет сам объект Date. Вместо этого выполняются преобразования:

  • UTC ⇄ локальная временная зона
  • UTC ⇄ IANA time zone (например, "Europe/Berlin", "Asia/Almaty")
  • локальное отображение без изменения исходного значения

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


Основные сущности

IANA Time Zone

Строковый идентификатор зоны, например:

  • "UTC"
  • "Europe/London"
  • "Asia/Tokyo"
  • "America/New_York"

Эти идентификаторы учитывают:

  • переходы на летнее/зимнее время
  • исторические изменения часовых поясов
  • региональные особенности смещения

Установка библиотеки

npm install date-fns date-fns-tz

Ключевые функции date-fns-tz

formatInTimeZone

Одна из основных функций библиотеки. Позволяет форматировать дату так, будто она находится в указанной временной зоне.

import { formatInTimeZone } from 'date-fns-tz'

const date = new Date('2026-01-01T12:00:00Z')

const result = formatInTimeZone(
  date,
  'Asia/Almaty',
  'yyyy-MM-dd HH:mm:ss XXX'
)

Функция:

  • не изменяет исходный Date
  • вычисляет корректное смещение зоны
  • применяет форматирование date-fns

toZonedTime

Преобразует UTC-время в “виртуальное локальное время” выбранной зоны.

import { toZonedTime } from 'date-fns-tz'

const utcDate = new Date('2026-01-01T12:00:00Z')

const zonedDate = toZonedTime(utcDate, 'Europe/Berlin')

Важно:

  • возвращаемый Date не является новым часовым поясом в прямом смысле
  • это пересчитанное время для отображения

fromZonedTime

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

import { fromZonedTime } from 'date-fns-tz'

const date = fromZonedTime('2026-01-01 12:00:00', 'Asia/Tokyo')

Используется при:

  • сохранении пользовательского ввода
  • нормализации времени перед отправкой на сервер

zonedTimeToUtc (устаревающий подход)

Ранее использовалась функция:

import { zonedTimeToUtc } from 'date-fns-tz'

Она выполняла аналогичное преобразование, но в современных версиях чаще заменяется fromZonedTime.


Проблема локального времени в JavaScript

Стандартный объект Date имеет ограничение: он не хранит информацию о временной зоне.

new Date('2026-01-01T12:00:00')

Интерпретация зависит от среды выполнения:

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

date-fns-tz решает эту проблему через явное указание зоны.


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

Пример: вывод даты в нескольких зонах

import { formatInTimeZone } from 'date-fns-tz'

const date = new Date('2026-06-01T12:00:00Z')

const london = formatInTimeZone(date, 'Europe/London', 'HH:mm')
const tokyo = formatInTimeZone(date, 'Asia/Tokyo', 'HH:mm')
const newYork = formatInTimeZone(date, 'America/New_York', 'HH:mm')

Каждое значение будет различаться из-за смещения и возможного DST.


Работа с летним временем (DST)

Одно из ключевых преимуществ — автоматический учёт переходов на летнее время.

Пример ситуации:

  • март: UTC+1
  • июль: UTC+2

Библиотека:

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

Типичные сценарии использования

Серверная нормализация времени

const utc = fromZonedTime('2026-03-10 09:00:00', 'Asia/Almaty')

Используется при:

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

Отображение расписаний

const eventTime = formatInTimeZone(
  event.date,
  user.timeZone,
  'dd.MM.yyyy HH:mm'
)

Позволяет:

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

Планирование задач

При планировании важно разделять:

  • момент сохранения (UTC)
  • момент отображения (локальная зона)
  • момент выполнения (серверное UTC)

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

date-fns-tz использует форматирование date-fns.

Часто используемые токены:

  • yyyy — год
  • MM — месяц
  • dd — день
  • HH — часы (24h)
  • mm — минуты
  • ss — секунды
  • XXX — смещение часового пояса
formatInTimeZone(date, 'UTC', 'yyyy-MM-dd HH:mm:ss XXX')

Отличие от Intl API

JavaScript содержит встроенный Intl.DateTimeFormat, однако date-fns-tz отличается:

  • более предсказуемое API
  • интеграция с date-fns
  • единый стиль форматирования
  • удобные преобразования между зонами

Intl:

  • нативный
  • менее гибкий для вычислений

date-fns-tz:

  • ориентирован на трансформации и логику времени

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

1. Хранение локального времени в базе

Проблема:

  • разное отображение на серверах

Решение:

  • хранение только UTC

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

Проблема:

  • сдвиги времени на 1 час

Решение:

  • использование IANA зон вместо фиксированных offset

3. Смешивание строкового и объектного времени

Проблема:

  • некорректные преобразования

Решение:

  • единый слой преобразования через date-fns-tz

Производительность и особенности реализации

date-fns-tz:

  • не содержит тяжёлых классов
  • работает поверх чистых функций
  • использует предрасчитанные данные IANA
  • не мутирует входные данные

Это делает библиотеку подходящей для:

  • серверных приложений
  • высоконагруженных API
  • фронтенд-рендеринга

Типовой поток работы с датами

  1. Получение времени (строка или Date)
  2. Преобразование в UTC (fromZonedTime)
  3. Сохранение в хранилище
  4. При выводе — форматирование (formatInTimeZone)

Работа с API и временными зонами пользователей

При работе с распределёнными системами критично фиксировать:

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

date-fns-tz выступает промежуточным слоем, исключающим неоднозначность интерпретации времени.