Большинство проблем с часовыми поясами при работе с датами в
JavaScript возникает не из-за самой библиотеки, а из-за особенностей
встроенного объекта Date и различий между локальным
временем, UTC и часовыми поясами среды выполнения. Библиотека date-fns
лишь формализует операции над датами, но не изменяет базовую модель
времени в языке.
Ключевая особенность: Date в JavaScript всегда хранит
момент времени в UTC, а отображение зависит от локальной зоны
окружения.
const date = new Date("2026-01-01T12:00:00Z");
console.log(date.toString()); // локальное представление
console.log(date.toISOString()); // UTC
Ошибки начинаются там, где ожидается сохранение «локального времени», но фактически сохраняется абсолютный момент.
Функции date-fns, такие как format,
parseISO, addDays, работают с объектом
Date, не хранящим информацию о часовом поясе. Это приводит
к неоднозначности при форматировании.
import { format } from "date-fns";
const date = new Date("2026-01-01T00:00:00Z");
format(date, "yyyy-MM-dd HH:mm:ss");
Результат зависит от локальной системы:
Фактически одна и та же дата интерпретируется по-разному.
Передача строки без временной зоны приводит к интерпретации как локального времени.
new Date("2026-01-01 10:00:00");
Такое значение не является UTC-строкой и будет обработано по правилам окружения.
Правильный формат:
new Date("2026-01-01T10:00:00Z");
JSON-форматирование часто уничтожает информацию о зоне.
const obj = {
date: new Date()
};
JSON.stringify(obj);
После восстановления:
const parsed = JSON.parse(json);
new Date(parsed.date);
Восстановленный объект не содержит информации о исходной временной зоне, только UTC-метку.
Функция format не учитывает временные зоны, она работает
с локальным представлением Date.
import { format } from "date-fns";
format(new Date("2026-01-01T00:00:00Z"), "dd.MM.yyyy HH:mm");
Если сервер и клиент находятся в разных зонах, результат будет различаться.
Для явного управления часовыми поясами используется дополнительный
пакет date-fns-tz.
Основная идея: отделить момент времени (UTC) от представления в конкретной зоне.
import { formatInTimeZone } from "date-fns-tz";
const date = new Date("2026-01-01T00:00:00Z");
formatInTimeZone(date, "Asia/Almaty", "yyyy-MM-dd HH:mm:ss");
Здесь происходит:
Первый шаг — фиксирование исходного представления:
console.log(date.toISOString());
console.log(date.toString());
console.log(Intl.DateTimeFormat().resolvedOptions().timeZone);
Это позволяет определить:
Частая проблема возникает при различии Node.js и браузера.
console.log(new Date().toISOString());
Сервер может работать в UTC, тогда как клиент — в локальной зоне пользователя.
Ошибки часто появляются не на входе, а после цепочки операций:
import { addDays } from "date-fns";
const base = new Date("2026-01-01T00:00:00Z");
const shifted = addDays(base, 1);
console.log(base.toISOString());
console.log(shifted.toISOString());
Любая операция date-fns сохраняет абсолютный момент, но теряется смысл «календарной даты» в локальной зоне.
Переходы на летнее и зимнее время создают нестабильность локальных вычислений.
const date = new Date("2026-03-29T02:30:00");
В некоторых зонах это время может не существовать или быть смещено.
Типичные последствия:
date-fns оперирует абсолютным временем, а не календарными представлениями.
import { addMonths } from "date-fns";
const date = new Date("2026-01-31T00:00:00Z");
const result = addMonths(date, 1);
Результат зависит от:
Для устойчивой логики используется подход:
const utcDate = new Date(Date.UTC(2026, 0, 1, 12, 0, 0));
const local = new Date(utcDate);
Это исключает накопление ошибок при цепочках преобразований.
parseISO из date-fns строго работает с ISO 8601, но не
решает проблему зон.
import { parseISO } from "date-fns";
const date = parseISO("2026-01-01T12:00:00Z");
Если строка не содержит Z или смещения, результат будет
зависеть от среды.
Практика устранения неоднозначностей:
const normalize = (d) => new Date(d.getTime());
const a = normalize(new Date());
const b = normalize(new Date(a.toISOString()));
Цель — всегда опираться на timestamp, а не строковое представление.
Ошибки часто возникают до date-fns:
Фиксация формата входных данных устраняет большую часть проблем:
ZСтабильная схема работы с date-fns строится вокруг одного принципа:
import { formatInTimeZone } from "date-fns-tz";
const formatSafe = (date) =>
formatInTimeZone(date, "UTC", "yyyy-MM-dd HH:mm:ss");
Различия между окружениями:
Эти различия приводят к несогласованности даже при одинаковом коде date-fns.
Для устранения неоднозначностей используется сравнение timestamp:
const d = new Date("2026-01-01T00:00:00Z");
console.log(d.getTime());
console.log(Date.now());
timestamp полностью исключает влияние часовых поясов.
Проблемы часто возникают при:
import { startOfDay, endOfDay } from "date-fns";
const start = startOfDay(new Date());
const end = endOfDay(new Date());
Эти функции опираются на локальную зону, что может давать разные результаты на сервере и клиенте.
Последовательность действий при диагностике:
toISOString() и toString()date-fns-tz при отображенииТакая модель устраняет большинство скрытых расхождений, возникающих при работе с date-fns и часовыми поясами в JavaScript.