Date смешивает слишком много понятий: момент времени, локальное представление, таймзону окружения, парсинг строк и мутабельность. В итоге баги часто появляются не в unit-тестах, а в production: на DST-переходах, у пользователей из другой таймзоны или при переносе кода между Node.js и браузером.
Temporal решает это за счёт явного разделения сущностей:
• Temporal.Instant — точный момент времени, удобно хранить в UTC
• Temporal.ZonedDateTime — дата/время + IANA timezone
• Temporal.PlainDate — календарная дата без времени и таймзоны
• Temporal.PlainTime — время без даты
• Temporal.Duration — длительность
• Temporal.PlainDateTime — локальные дата и время без привязки к зоне
Главная идея: не притворяться, что «дата» — это одна универсальная сущность.
Пример с DST:
import { Temporal } from '@js-temporal/polyfill';
const meeting = Temporal.ZonedDateTime.from(
'2024-03-30T12:00:00+01:00[Europe/Berlin]'
);
console.log(meeting.add({ days: 1 }).toString());
// 2024-03-31T12:00:00+02:00[Europe/Berlin]
console.log(meeting.add({ hours: 24 }).toString());
// 2024-03-31T13:00:00+02:00[Europe/Berlin]Это не одно и то же.
add({ days: 1 }) означает «завтра в это же локальное время». add({ hours: 24 }) означает «через 24 реальных часа».На переходе на летнее время день может быть не 24 часа. Temporal делает это различие явным, а Date обычно прячет проблему до момента, когда пользователи начинают жаловаться.
Ещё один важный кейс — несуществующее локальное время:
try {
Temporal.ZonedDateTime.from(
'2024-03-31T02:30[Europe/Berlin]',
{ disambiguation: 'reject' }
);
} catch {
console.log('Такого локального времени нет из-за DST');
}В ночь перехода на летнее время 02:30 в Berlin может просто не существовать. В Date такие ситуации часто «нормализуются» молча. В Temporal можно явно выбрать стратегию:
reject, earlier, later, compatible.Практические правила для production:
1. Храните точный момент как
Instant Например, событие уже произошло или платёж создан.
2. Храните пользовательскую таймзону отдельно
Не
GMT+3, а IANA zone: Europe/Berlin, Asia/Tbilisi, America/New_York.3. Для бизнес-правил используйте календарные типы
День рождения — это
PlainDate, а не Date в полночь UTC. «Каждый день в 09:00 по Москве» — это локальное время + timezone, а не
setInterval(24h).4. Не заменяйте календарные операции миллисекундами
+ 86400000 — это не всегда «завтра». На DST это может быть 23 или 25 локальных часов.5. Делайте неоднозначность явной
Если локальное время может не существовать или повторяться, выбирайте поведение явно через
disambiguation.Temporal особенно полезен там, где цена ошибки высокая: расписания, биллинг, бронирования, напоминания, cron-like задачи, SLA, отчёты по локальным дням.
Важно: поддержка Temporal зависит от runtime. Для production сейчас обычно используют polyfill
@js-temporal/polyfill и постепенно готовятся к нативной поддержке.Коротко: Date хорош для простых timestamp-операций, но плохо моделирует реальные бизнес-даты. Temporal заставляет выбрать правильную модель времени — и именно поэтому снижает количество багов на таймзонах и DST.
