Работа с датами в JavaScript неизбежно упирается в проблему часовых
поясов: одинаковое значение Date может отображаться
по-разному в зависимости от локали среды выполнения, настроек сервера
или браузера. Библиотека date-fns предоставляет инструменты
форматирования дат, но сама по себе не решает задачу корректного
отображения времени в произвольных timezone. Для этого используется
расширение date-fns-tz, которое добавляет операции преобразования и
форматирования с учётом временных зон.
Встроенный объект Date хранит момент времени в виде
количества миллисекунд от Unix epoch (UTC). Однако:
Date.toString() зависит от локальной timezone
среды;Date.toISOString() всегда возвращает UTC;getHours, getDate)
используют локальную timezone.Ключевая проблема: один и тот же момент времени интерпретируется по-разному в разных регионах.
Пример:
const d = new Date("2026-01-01T12:00:00Z");
console.log(d.toString());
// зависит от локальной timezone
console.log(d.toISOString());
// всегда: 2026-01-01T12:00:00.000Z
При построении систем логирования, календарей и расписаний этого недостаточно: требуется явное управление timezone.
date-fns предоставляет функцию format, которая работает
только с локальной timezone среды выполнения.
import { format } from "date-fns";
const d = new Date(2026, 0, 1, 15, 30);
format(d, "yyyy-MM-dd HH:mm:ss");
Ключевые токены:
yyyy — годMM — месяц (01–12)dd — день месяцаHH — часы (24-часовой формат)mm — минутыss — секундыФункция не принимает timezone параметр, поэтому:
format(new Date("2026-01-01T12:00:00Z"), "HH:mm");
будет зависеть от локального времени пользователя или сервера.
В распределённых системах один и тот же timestamp может интерпретироваться по-разному:
Без явной timezone логика становится неоднозначной:
date-fns-tz добавляет функции:
formatInTimeZonetoZonedTimefromZonedTimeЭти функции позволяют отделить:
formatInTimeZone(date, timeZone, formatString)
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"
);
console.log(result);
date — исходный момент времени (UTC)"Asia/Almaty" — целевая timezoneОсобое значение имеют:
XXX — смещение +05:00zzzz — полное название timezonez — краткое обозначение (если доступно)Правильная архитектура работы с датами:
Всегда UTC:
const stored = new Date().toISOString();
Всё вычисляется в UTC:
const timestamp = Date.now();
Только через timezone:
formatInTimeZone(timestamp, "Europe/Berlin", "yyyy-MM-dd HH:mm");
Преобразует момент времени в “виртуальное локальное время” указанной зоны:
import { toZonedTime } from "date-fns-tz";
const utcDate = new Date("2026-01-01T12:00:00Z");
const zoned = toZonedTime(utcDate, "America/New_York");
console.log(zoned);
Важно: возвращается обычный Date, но интерпретируемый
как локальное время указанной зоны при форматировании.
Создаёт UTC момент из локального времени конкретной зоны:
import { fromZonedTime } from "date-fns-tz";
const date = fromZonedTime(
"2026-01-01 10:00:00",
"Europe/Paris"
);
Используется при вводе данных пользователем в определённой зоне.
Часто требуется отображать не только локальное время, но и смещение.
formatInTimeZone(date, "Asia/Tokyo", "yyyy-MM-dd HH:mm XXX");
Результат:
2026-01-01 21:00 +09:00
formatInTimeZone(
date,
"America/Los_Angeles",
"yyyy-MM-dd HH:mm:ss 'GMT' XXX"
);
Вывод:
2026-01-01 04:00:00 GMT -08:00
В системах с профилями пользователей timezone часто хранится как строка:
const user = {
timezone: "Europe/Moscow"
};
Использование:
formatInTimeZone(
Date.now(),
user.timezone,
"HH:mm dd.MM.yyyy"
);
format(new Date(), "yyyy-MM-dd HH:mm");
Проблема: результат зависит от сервера.
date.setHours(date.getHours() + 3);
Проблема: не учитывает переходы DST и сложные зоны.
Проблема приводит к несогласованности данных между регионами.
Timezone — это не фиксированное смещение. Например:
date-fns-tz учитывает DST автоматически через IANA timezone database.
Пример:
formatInTimeZone(date, "Europe/Berlin", "yyyy-MM-dd HH:mm XXX");
Результат будет отличаться в зависимости от сезона.
format(date, "HH:mm");
formatInTimeZone(date, "UTC", "HH:mm");
date-fns часто используется вместе с:
addDayssubHoursdifferenceInMinutesstartOfDayПример:
import { addDays } from "date-fns";
import { formatInTimeZone } from "date-fns-tz";
const nextWeek = addDays(new Date(), 7);
formatInTimeZone(nextWeek, "Asia/Almaty", "yyyy-MM-dd");
formatInTimeZone(new Date(), "UTC", "yyyy-MM-dd HH:mm:ss");
formatInTimeZone(new Date(), user.timezone, "yyyy-MM-dd HH:mm:ss XXX");
Используются стандартные идентификаторы:
UTCEurope/LondonAsia/TokyoAmerica/New_YorkЭти значения являются частью IANA Time Zone Database и обеспечивают корректную обработку исторических изменений.
date-fns — базовые операции с датамиdate-fns-tz — контроль timezone и корректное
отображениеformatInTimeZonefromZonedTime