Интеграция с date-fns-tz

Работа с датами в JavaScript неизбежно упирается в проблему временных зон, поскольку встроенный объект Date оперирует UTC-ориентированным внутренним представлением и локальной зоной окружения, не предоставляя полноценного API для корректного управления IANA time zone идентификаторами (например, Europe/Amsterdam, Asia/Almaty). Библиотека date-fns-tz расширяет функциональность date-fns, добавляя инструменты для преобразования времени между зонами, форматирования с учётом часового пояса и конвертации локального времени в UTC и обратно.

Архитектурная модель интеграции

date-fns строится как набор чистых функций, работающих с объектом Date, не изменяя его прототип. date-fns-tz следует той же философии: вместо расширения Date добавляются функции-обёртки, принимающие дату, строку временной зоны и возвращающие новый результат.

Ключевой принцип интеграции:

  • date-fns — работа с календарной логикой (дни, месяцы, интервалы)
  • date-fns-tz — работа с географическими временными зонами

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

Установка и подключение

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

npm install date-fns date-fns-tz

Импорт функций выполняется точечно:

import { format, addDays } from "date-fns";
import { formatInTimeZone, zonedTimeToUtc, utcToZonedTime } from "date-fns-tz";

Подход с именованными импортами снижает размер бандла и поддерживает tree-shaking.

Основные концепции временных зон

IANA Time Zone

date-fns-tz опирается на IANA Time Zone Database. В отличие от фиксированных смещений (+06:00), IANA зоны учитывают:

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

Пример идентификаторов:

  • Europe/Moscow
  • Asia/Almaty
  • America/New_York

Конвертация UTC → локальная зона

Функция utcToZonedTime (или в новых версиях toZonedTime) преобразует UTC-время в объект, интерпретируемый в заданной зоне.

import { utcToZonedTime } from "date-fns-tz";

const utcDate = new Date("2026-05-22T10:00:00Z");
const zoned = utcToZonedTime(utcDate, "Asia/Almaty");

Важно: возвращаемый объект Date не хранит зону, он лишь пересчитывает значения компонентов даты для указанного контекста.

Конвертация локального времени → UTC

Функция zonedTimeToUtc выполняет обратное преобразование:

import { zonedTimeToUtc } from "date-fns-tz";

const localDate = new Date("2026-05-22T10:00:00");
const utcDate = zonedTimeToUtc(localDate, "Asia/Almaty");

Механика работы:

  • входное локальное время трактуется как принадлежащее указанной зоне
  • вычисляется соответствующее UTC-время
  • создаётся новый объект Date

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

Функция formatInTimeZone является одной из наиболее значимых в интеграции:

import { formatInTimeZone } from "date-fns-tz";

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

const formatted = formatInTimeZone(
  date,
  "Asia/Almaty",
  "yyyy-MM-dd HH:mm:ssXXX"
);

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

  • форматирование происходит без промежуточного изменения Date
  • учитываются правила DST (daylight saving time)
  • поддерживаются токены формата из date-fns

Связка с date-fns форматированием

date-fns предоставляет мощную систему токенов:

  • yyyy — год
  • MM — месяц
  • dd — день
  • HH — часы
  • mm — минуты
  • XXX — смещение часового пояса

date-fns-tz расширяет это поведение, позволяя применять форматирование в контексте конкретной зоны без изменения глобального состояния.

Обработка летнего времени (DST)

Одной из сложных задач является корректная обработка переходов:

  • весенний сдвиг (час “исчезает”)
  • осенний сдвиг (час повторяется)

Пример:

import { zonedTimeToUtc } from "date-fns-tz";

const ambiguous = zonedTimeToUtc(
  new Date("2026-10-25T02:30:00"),
  "Europe/Berlin"
);

В таких случаях библиотека использует правила IANA, определяя фактическое UTC-представление.

Типичные сценарии интеграции

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

На сервере все значения приводятся к UTC:

const utc = zonedTimeToUtc(inputDate, userTimeZone);

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

Отображение пользовательского времени

const display = formatInTimeZone(
  storedUtcDate,
  userTimeZone,
  "dd.MM.yyyy HH:mm"
);

Планирование событий

При создании событий важно различать:

  • момент в UTC (истина системы)
  • представление в локальной зоне (UX слой)
const eventUtc = zonedTimeToUtc(localInput, "Asia/Almaty");

Сравнение подходов: naive Date vs date-fns-tz

Без date-fns-tz:

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

С использованием:

  • явное указание зоны
  • детерминированные преобразования
  • отсутствие зависимости от окружения Node.js/браузера

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

При одновременной обработке разных регионов типичный подход:

const zones = ["Asia/Almaty", "Europe/Paris", "America/New_York"];

const results = zones.map(zone =>
  formatInTimeZone(new Date(), zone, "HH:mm")
);

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

Особенности версии и API различия

В экосистеме date-fns-tz существуют изменения между версиями:

  • ранние версии: utcToZonedTime, zonedTimeToUtc
  • новые версии: toZonedTime, fromZonedTime

Несмотря на различия в именах, концепция остаётся одинаковой: явное преобразование между UTC и IANA-зоной без мутаций исходного объекта.

Ограничения и поведенческие нюансы

Интеграция с date-fns и date-fns-tz не решает всех проблем работы с временем:

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

При этом модель остаётся предсказуемой при соблюдении принципа: UTC как источник истины, IANA зона как слой представления.