Temporal в ES2026: конец мучениям с датами в JavaScript

На заседании TC39 в марте 2026 года API Temporal достиг стадии Stage 4 и официально вошёл в спецификацию ECMAScript 2026. Так завершился путь, начавшийся ещё в 2017 году — почти десятилетие работы, — чтобы исправить одну из старейших болей языка: объект Date.

Проблема: Date так и не повзрослел

Любой, кто работал с датами в JavaScript, знает эту боль. Date изменяемый (mutable), не имеет настоящей поддержки часовых поясов и ведёт себя настолько запутанно, что стандартным решением было подключить тяжёлую стороннюю библиотеку вроде Moment.js — порой сотни килобайт данных о часовых поясах — лишь ради того, чтобы «прибавить два часа с учётом перехода на летнее время».

Знакомый итог: тихие ошибки в расчётах времени или полный отказ от Date.

Что изменилось: пространство имён Temporal

Temporal вводит новое пространство имён со специализированными типами, и каждое значение в нём неизменяемо (immutable) — любая операция возвращает новое значение, а не меняет исходное. Ключевые типы:

  • Temporal.Instant — точный момент на глобальной шкале времени.
  • Temporal.ZonedDateTime — дата и время, привязанные к конкретному часовому поясу, с корректной обработкой перехода на летнее время.
  • Temporal.PlainDate, Temporal.PlainTime, Temporal.PlainDateTime — календарные значения без часового пояса (например, «24 июля» в отрыве от зоны).
  • Temporal.Duration — длительность для арифметики без библиотек.
  • Temporal.Now — явная точка входа для текущего момента.

Практический пример

import { Temporal } from '@js-temporal/polyfill';
// пока нативная поддержка не появилась везде

// Дата без часового пояса
const date = Temporal.PlainDate.from('2026-07-24');
date.add({ days: 10 }).toString();
// '2026-08-03' — значение неизменяемо, поэтому возвращается новое

// Точный момент, привязанный к часовому поясу
const meeting = Temporal.ZonedDateTime.from({
timeZone: 'Asia/Riyadh',
year: 2026, month: 7, day: 24, hour: 16, minute: 0,
});

// Перевод в другую зону — переход на летнее время учтён верно
meeting.withTimeZone('Europe/Moscow').toString();

// Арифметика длительностей без внешних библиотек
const dur = Temporal.Duration.from({ hours: 3, minutes: 30 });
meeting.add(dur).toString();

// «Сейчас» стало явным и зависящим от зоны
Temporal.Now.zonedDateTimeISO('Asia/Riyadh').toString();

Ключевое отличие: никакой скрытой мутации значений, а часовой пояс — полноценная часть типа, а не «прицеп» сбоку.

Нюансы из реальной практики

Temporal уже в спецификации, но поддержка ещё разворачивается:

  • Браузеры: Firefox уже поставил его, браузеры на Chromium (Chrome, Edge) поддерживают начиная с Chromium 144. У Safari частичная поддержка в Technology Preview.
  • TypeScript: версия 6.0 включает определения типов Temporal.
  • Node.js: поддержка ожидается в будущем релизе; до тех пор используйте официальный @js-temporal/polyfill.

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

Вывод

Temporal — не просто замена Date, а полное переосмысление того, как JavaScript работает со временем: неизменяемые значения, часовые пояса «из коробки» и встроенная арифметика длительностей. Переход на него снижает число ошибок и снимает зависимость от тяжёлых библиотек. Начните с полифила в одном проекте и пишите новую логику времени сразу на Temporal.

Если вы собираете современный набор инструментов, Temporal дополняет более широкую картину эволюции экосистемы — как и TypeScript 7 с его нативным компилятором.

Первоисточник: Предложение Temporal — TC39 (GitHub)