Работа с датами, временем и Temporal

25.01.2026 • js

Вступление

Temporal потихоньку появляется во всех браузерах: в Хроме завезли в 144й версии, в Файерфоксе он вышел чуть раньше, в 139й версии, а в Сафари идёт в замыкающих, в нём работа активно ведётся и сейчас требуется Technical Preview с флагом.

Temporal является абсолютно новым апи, не имеет отношения к Date и решает многие его проблемы. Также он призван добавить новые возможности, которых не хватало долгие годы и ради которых активно использовались сторонние (и довольно тяжёловесные) библиотеки.

Однако при ближайшем рассмотрении окажется, что основным нововведением является только работа с таймзонами. Поэтому в этой статье посмотрим на основные задачи со временем, с которыми сталкиваются разработчики, и посмотрим, как к ним применим Temporal. Мы не будем пытаться разобрать всё развесистое апи Temporal со всеми его нюансами, вместо этого мы посмотрим на его применение на практике.

Подсчёт разницы во времени

Довольно часто требуется посчитать разницу между моментами времени, например, для какой-либо аналитики, для подсчёта скоростных метрик, или для продуктовой логики (в качестве примера, когда задержка таймера рассчитывается исходя из прошедшего времени).

Частый способ это реализовать — Date.now() (или просто new Date().getTime()):

const start = Date.now();
 
...
 
const delta = Date.now() - start;

Результат — в миллисекундах. При ближайшем рассмотрении у данного кода есть один весомый минус: время в Date.now() не является монотонным и зависит от настроек системы, поэтому в примере выше можно получить отрицательную дельту.

Как применить в данном месте Temporal?

const start = Temporal.Now.instant();
 
...
 
const end = Temporal.Now.instant();
const duration = end.since(start, { largestUnit: 'milliseconds' });
// PT114.639S
console.log(duration.milliseconds);
// 114639

Интересный момент — можно получить не только миллисекунды, но и нано-, если вдруг понадобится. Только нужно иметь ввиду, что браузеры могут защищаться от фингерпринтинга и “округлять” значения, поэтому результат может быть неточным.

Самый же главный минус тот же самый, что и у Date.now(): это не монотонное время и можно получить отрицательную продолжительность. Какой же лучший способ сделать? Вероятно, performance.now():

const start = performance.now();
 
...
 
const delta = performance.now() - start;

performance.now() возвращает время в миллисекундах, и это дробное число, поэтому точность может быть выше, чем в Date.now(). Также время является монотонным и не имеет отношения к настройкам системы, а только ко времени жизни страницы или воркера. Поддержка очень хорошая, функцию можно использовать уже сейчас.

Форматирование момента времени

Очень давно можно было регулярно встретить код, который был нужен для вывода даты / времени:

const months = ['январь', 'февраль', 'март', ...];
 
const str = date.getDate() + ' ' + months[date.getMonth()] + ' ' + date.getFullYear();

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

Как решить ту же задачу сейчас, с Temporal?

const temporal = Temporal.Instant.from('2026-01-25T12:00:00Z').toZonedDateTimeISO('Europe/Moscow');
// Объект с типом Temporal.ZonedDateTime
temporal.toLocaleString('ru-RU', {
    dateStyle: 'long',
    timeStyle: 'short'
});
// 25 января 2026 г. в 15:00

Думаю, опции будут кому-то знакомы — они точно такие же, как в Intl.DateTimeFormat. Плюсы очевидны:

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

Если мы упомянули Intl.DateTimeFormat, то что с ним и нельзя ли его использовать для этого?

const date = Date.parse('2026-01-25T12:00:00Z');
const format = new Intl.DateTimeFormat('ru-RU', {
    dateStyle: 'long',
    timeStyle: 'short',
    timeZone: 'Europe/Moscow'
});
format.format(date);
// 25 января 2026 г. в 15:00

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

Поддержка у Intl.DateTimeFormat (включая таймзону) очень хорошая.

Интеграция Temporal и Intl.DateTimeFormat

Intl.DateTimeFormat может форматировать не только Date, но ряд объектов Temporal: Temporal.PlainDateTime, Temporal.PlainTime, Temporal.PlainDate, Temporal.PlainYearMonth и Temporal.PlainMonthDay. Можно заметить, что Temporal.ZonedDateTime в списке нет, форматировать дату-время с таймзоной с помощью этого формата нельзя. Поэтому можно или конвертировать в значение без таймзоны, или не добавлять её вовсе:

const temporal = Temporal.Instant.from('2026-01-25T12:00:00Z');
const format = new Intl.DateTimeFormat('ru-RU', {
    dateStyle: 'long',
    timeStyle: 'short',
    timeZone: 'Europe/Moscow'
});
format.format(temporal);

В этом случае формат выглядит практически также, как и исходная запись с Date.

Форматирование промежутка времени

Ещё одной частой задачей является вывод промежутка времени, например, “3 часа”. Для этого можно было использовать тот же moment.js или другие библиотеки. Посмотрим на вариант с Temporal:

const duration = Temporal.Duration.from({ hours: 3});
duration.toLocaleString('ru-RU', { style: 'long' });
// 3 часа

Тоже самое можно получить и с Intl:

const duration = {
    hours: 3
};
 
new Intl.DurationFormat('ru-RU', { style: 'long' }).format(duration);

DurationFormat гораздо более новый, чем его собрат, DateTimeFormat, поэтому обращайте внимание на поддержку. Но это всё ещё лучше, чем поддержка Temporal.

Ещё чаще требуется вывести разницу во времени относительно текущего момента, например, “3 часа назад”.

Для этого уже нет встроенной поддержки в Temporal, есть только Intl.RelativeTimeFormat:

new Intl.RelativeTimeFormat("ru-RU", { style: "long" }).format(-3, 'hour');
// 3 часа назад 

Поддержка хорошая.

Какие всё-таки плюсы у Temporal?

В примерах выше мы посмотрели задачи, которые часто встречаются разработчикам, и как они решаются с Temporal, а также, как они решаются без него. Так в чём же всё-таки плюсы Temporal, и чем он делает нашу жизнь лучше?

Думаю, что главное — это сведение всех апи в одну новую систему. Нужен момент времени — пожалуйста. Промежуток — тоже. Дата без конкретной таймзоны тоже есть. Если раньше приходилось теми или иными способами использовать Date, чтобы получить нужное, и это очень сильно путало разработчиков (где есть таймзона, где её нет и так далее), то с Temporal мы можем использовать другую ментальную модель, которая ближе к задаче, чем Date.

Ещё один плюс — поддержка других календарей. И хоть это не является особенной проблемой для разработчика из России, думаю, в определённых странах мира и под определённые задачи это будет жирным плюсом.

Плюсом будет и неизменность (иммутабельность). В эпоху Реакта с его перерисовками дата часто стояла особняком, ведь с ней могло произойти что угодно, и компонент не перерисуется. С Temporal должно быть проще.

Ещё один момент связан с методом .add() у нескольких видов объектов Temporal. Мы наконец-то получили возможность сдвинуть момент времени на месяц вперёд в явном виде, а не использовать код в духе date.setMonth(date.getMonth() + 1), который ещё и является некорректным в некоторых случаях (например, мы можем хотеть сделать это в рамках определённой таймзоны).

Наконец, мы решим ещё одну давнюю боль — Invalid date. Если дату создать не удалось, то объект окажется в состоянии ошибки, и может спокойно уходить в другие функции и расползаться по коду. И уже в совершенно другом месте мы можем обнаружить, что нам нужна проверка на Invalid date:

const invalidDate = new Date('a');
const isInvalid = Number.isNaN(invalidDate.getTime());

Temporal выкинет ошибку, если дату не удастся распарсить, поэтому такой ситуации больше быть не должно.

Итого

Temporal уже практически у нас на пороге и является сложным апи, который потребует привыкания от разработчиков, а в чём-то — и переучивания. Однако многие задачи можно решить уже сейчас, во всех браузерах, и без Temporal, просто нужно знать подходящий инструмент.

Зачем же нужен Temporal? Чтобы создать правильный фундамент для будущего веба. Фундамент, в котором мы будем работать с таймзонами правильно, будем правильно считать время и поддерживать другие календари. Фундамент, с которым у нас не останется костылей с вычитанием оффсета таймзоны из даты и тому подобного. Он должен сделать работу нашего кода правильнее, а сам код — читаемее и понятнее. Остаётся только беспокоиться, что получится более многословно.

Обсудить в Telegram

Почитать ещё посты

  • Отслеживание поддержки фич
  • gesturechange и зум тачпадом на маках
  • Map() становится удобнее
  • Маркировка контента, сделанного с помощью ИИ
  • Отрисовка текста и её обновки в браузерах