Temporal API

Temporal API представляет собой современную альтернативу встроенному объекту Date, устраняющую множество исторических проблем работы с датой и временем в JavaScript. Ключевая идея заключается в разделении понятий: календарные даты, точное время, временные зоны и длительности представлены разными специализированными типами, а не одной перегруженной сущностью.

Проблемы классического Date и контекст появления Temporal

Объект Date в JavaScript исторически объединяет несколько различных концепций времени:

  • локальное время системы и UTC одновременно
  • отсутствие явного разделения календарной даты и момента времени
  • мутирующее поведение методов
  • неочевидная работа с часовыми поясами
  • сложность арифметики дат (добавление месяцев, лет, дней)

Day.js частично решает эти проблемы через неизменяемость и удобный API, но всё равно опирается на Date внутри.

Temporal вводит строгую модель данных:

  • PlainDate — календарная дата без времени
  • PlainTime — время без даты
  • PlainDateTime — дата и время без временной зоны
  • ZonedDateTime — дата и время с часовым поясом
  • Instant — точный момент времени в UTC
  • Duration — длительность

Такое разделение устраняет неоднозначности и делает операции предсказуемыми.


Day.js и философия работы со временем

Day.js строится вокруг нескольких ключевых принципов:

  • неизменяемость объектов (immutability)
  • цепочечный API
  • компактность (минимальный размер библиотеки)
  • совместимость с Moment.js по стилю

Внутри Day.js всё равно используется Date, но библиотека скрывает сложность за единым интерфейсом.

Пример базовой работы:

import dayjs from 'dayjs';

const now = dayjs();
const future = now.add(2, 'day');
const formatted = future.format('YYYY-MM-DD HH:mm');

Несмотря на удобство, Day.js не решает фундаментальные проблемы временных зон и различия типов времени, которые и закрывает Temporal.


Концептуальное различие Day.js и Temporal

Модель данных

Day.js

  • один универсальный объект
  • внутренне основан на Date
  • тип времени неявный

Temporal

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

Работа с временными зонами

Day.js требует плагинов:

import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

dayjs.extend(utc);
dayjs.extend(timezone);

dayjs().tz('Asia/Tokyo');

Temporal делает это частью базовой модели через ZonedDateTime.


Форматы представления времени

Day.js: строковое форматирование

dayjs().format('DD.MM.YYYY');

Форматирование полностью отделено от типа данных.

Temporal: разделение данных и представления

Temporal разделяет хранение и форматирование:

  • объект времени не форматирует сам себя
  • преобразование выполняется отдельно через Intl API
const date = new Temporal.PlainDate(2026, 5, 23);

new Intl.DateTimeFormat('ru-RU').format(date.toPlainDateTime(0));

Интероперабельность Day.js и Temporal

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

Преобразование Temporal → Day.js

Основной подход — переход через ISO-строку или timestamp:

const instant = Temporal.Now.instant();
const jsDate = new Date(instant.epochMilliseconds);
const d = dayjs(jsDate);

Либо через строку:

const d = dayjs(instant.toString());

Преобразование Day.js → Temporal

Так как Day.js оборачивает Date, используется обратное преобразование:

const d = dayjs();
const instant = Temporal.Instant.fromEpochMilliseconds(d.valueOf());

или через Date:

const instant = Temporal.Instant.from(d.toDate().toISOString());

Основные сценарии интеграции

1. Существующие приложения на Day.js

Day.js часто используется в проектах, где:

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

Temporal в таких случаях может внедряться точечно:

  • новые модули используют Temporal
  • старые продолжают использовать Day.js

2. Гибридная модель

Типичная архитектура:

  • Day.js — форматирование, UI-слой
  • Temporal — бизнес-логика времени

Пример:

// бизнес-слой
const start = Temporal.Now.zonedDateTimeISO('Europe/Moscow');
const end = start.add({ hours: 3 });

// UI-слой
const formatted = dayjs(end.toString()).format('HH:mm');

Работа с длительностями

Day.js duration (через plugin)

import duration from 'dayjs/plugin/duration';
dayjs.extend(duration);

const d = dayjs.duration(2, 'hours');

Ограничения:

  • слабая типизация
  • отсутствие строгих единиц

Temporal Duration

const dur = new Temporal.Duration(0, 0, 0, 0, 2);

Преимущества:

  • строгая модель
  • отсутствие неоднозначности
  • поддержка сложной арифметики календаря

Календарная арифметика

Проблема Day.js

Добавление месяцев зависит от длины месяца:

dayjs('2024-01-31').add(1, 'month'); // 29 февраля или 2 марта

Поведение зависит от внутренних правил Date.

Temporal поведение

Temporal явно определяет результат:

const date = new Temporal.PlainDate(2024, 1, 31);
date.add({ months: 1 });

Результат вычисляется по календарной модели, а не по эвристике Date.


Часовые пояса как отдельная сущность

Day.js:

  • временная зона — плагин
  • базовая модель не учитывает её

Temporal:

  • ZonedDateTime включает:

    • локальную дату
    • смещение
    • идентификатор зоны
const zdt = Temporal.Now.zonedDateTimeISO('Asia/Almaty');

Это устраняет необходимость в сторонних абстракциях.


Форматы хранения и сериализация

Day.js

JSON.stringify(dayjs().toISOString());

или

dayjs().valueOf();

Temporal

const json = Temporal.Now.instant().toString();

или структурированное представление:

const obj = Temporal.Now.plainDateISO().toJSON();

Temporal обеспечивает более предсказуемую сериализацию без потери семантики.


Архитектурные рекомендации при переходе

Постепенная миграция

Day.js часто остается в системе как слой совместимости:

  • UI продолжает использовать форматирование Day.js
  • Temporal используется для вычислений
  • конвертация выполняется на границах слоёв

Разделение ответственности

  • Temporal:

    • вычисления
    • бизнес-логика времени
    • временные зоны
  • Day.js:

    • отображение
    • форматирование строк
    • совместимость со старым кодом

Ограничения совместного использования

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

  • различие в типах объектов (Temporal ≠ Date)
  • необходимость явных преобразований
  • риск двойной логики временных зон
  • потенциальные ошибки сериализации

Эти проблемы решаются строгими границами между слоями приложения.


Роль Day.js в эпоху Temporal

Day.js сохраняет актуальность в сценариях:

  • лёгкие frontend-приложения
  • быстрые прототипы
  • UI-ориентированное форматирование
  • минимальные зависимости

Temporal ориентирован на:

  • серверную бизнес-логику
  • финансовые и расписательные системы
  • системы с критичной точностью времени
  • многозональные вычисления

Обе модели могут сосуществовать, но решают разные уровни задач: Day.js — представление, Temporal — семантика времени.