Конвертация между часовыми поясами

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

Ключевая концепция заключается в разделении трёх представлений времени:

  • UTC-время — универсальное координированное время без смещения
  • Локальное время — время окружения выполнения (браузер или сервер)
  • Зональное время — время конкретной временной зоны (например, Asia/Almaty, Europe/Berlin)

Установка и подключение date-fns-tz

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

npm install date-fns date-fns-tz

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

import { format, parseISO } from 'date-fns';
import { zonedTimeToUtc, utcToZonedTime, formatInTimeZone } from 'date-fns-tz';

Представление времени в JavaScript

Объект Date в JavaScript хранит момент времени в виде количества миллисекунд с эпохи Unix (UTC). При выводе значения автоматически преобразуются в локальную временную зону среды выполнения.

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

console.log(date.toString()); 
// локальное представление времени

console.log(date.toISOString());
// всегда UTC

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


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

Функция zonedTimeToUtc выполняет преобразование времени, интерпретируемого в указанной временной зоне, в UTC.

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

const dateInAlmaty = '2026-05-22 10:00:00';
const timeZone = 'Asia/Almaty';

const utcDate = zonedTimeToUtc(dateInAlmaty, timeZone);

console.log(utcDate.toISOString());

Механика преобразования

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

Использование важно при сохранении пользовательского ввода в базе данных, где требуется единый стандарт хранения времени.


Преобразование UTC в локальное время зоны

Функция utcToZonedTime позволяет получить объект Date, интерпретируемый в рамках конкретной временной зоны.

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

const utcDate = new Date('2026-05-22T12:00:00Z');
const timeZone = 'Asia/Tokyo';

const zonedDate = utcToZonedTime(utcDate, timeZone);

console.log(zonedDate);

Важно учитывать, что сам объект Date не хранит информацию о временной зоне. Преобразование влияет только на интерпретацию при форматировании.


Форматирование времени в конкретной временной зоне

Наиболее практичный инструмент — formatInTimeZone. Он позволяет сразу отобразить дату в нужной временной зоне без промежуточных преобразований.

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

const date = new Date('2026-05-22T12:00:00Z');
const timeZone = 'Europe/Berlin';

const result = formatInTimeZone(date, timeZone, 'yyyy-MM-dd HH:mm:ssXXX');

console.log(result);

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

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

Работа с идентификаторами временных зон IANA

Все операции выполняются с использованием стандартов IANA Time Zone Database:

Примеры:

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

Использование сокращений (например, MSK, PST) не поддерживается, поскольку они неоднозначны и зависят от контекста.


Форматирование с учётом смещения

formatInTimeZone поддерживает токены форматирования date-fns, включая отображение смещения.

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

const date = new Date('2026-05-22T12:00:00Z');

const formatted = formatInTimeZone(date, 'America/New_York', 'yyyy-MM-dd HH:mm:ss XXX');

Символ XXX отображает смещение относительно UTC, например -04:00.


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

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

import { zonedTimeToUtc, formatInTimeZone } from 'date-fns-tz';

const input = '2026-12-01 09:00:00';
const sourceZone = 'Asia/Almaty';

const utc = zonedTimeToUtc(input, sourceZone);

const formattedInNY = formatInTimeZone(utc, 'America/New_York', 'yyyy-MM-dd HH:mm:ss');

Цепочка преобразований:

  1. строка → локальное время исходной зоны
  2. локальное время → UTC
  3. UTC → отображение в другой зоне

Особенности летнего времени

При использовании date-fns-tz автоматически учитываются переходы Daylight Saving Time (DST). Это означает:

  • смещения могут изменяться в течение года
  • одинаковые локальные времена могут соответствовать разным моментам UTC
  • корректность зависит от актуальной базы IANA

Пример:

formatInTimeZone(
  new Date('2026-07-01T12:00:00Z'),
  'Europe/Berlin',
  'yyyy-MM-dd HH:mm:ss'
);

Летнее время будет учтено автоматически.


Сравнение подходов

Без учёта временных зон

new Date('2026-05-22T12:00:00')
  • интерпретация зависит от окружения
  • возможны ошибки при переносе между серверами

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

new Date('2026-05-22T12:00:00Z')
  • фиксированное глобальное время
  • подходит для хранения

С date-fns-tz

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

Частые сценарии преобразований

Сохранение пользовательского ввода

const utc = zonedTimeToUtc(userInput, userTimeZone);

Отображение события пользователю

const view = formatInTimeZone(eventDate, userTimeZone, 'dd.MM.yyyy HH:mm');

Переключение между регионами

const londonTime = formatInTimeZone(date, 'Europe/London', 'HH:mm');
const tokyoTime = formatInTimeZone(date, 'Asia/Tokyo', 'HH:mm');

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

date-fns использует токены форматирования:

  • yyyy — год
  • MM — месяц
  • dd — день
  • HH — часы (24-часовой формат)
  • mm — минуты
  • ss — секунды

При использовании с date-fns-tz форматирование происходит уже в контексте выбранной временной зоны, а не локальной среды выполнения.


Ограничения модели времени JavaScript

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

Эти ограничения компенсируются функциями date-fns-tz, которые выполняют вычисления смещений на уровне форматирования и преобразования.