Введение в JavaScript Temporal API

Spread the love

Каким бы ни было ваше мнение о JavaScript, но всем известно, что работа с датами на чистом JS — это большая головная боль. Реализация объекта Date() была скопирована непосредственно из Java в 1995 году. Два года спустя Java отказалась от нее, но она осталась в JavaScript для обратной совместимости.

Проблемы, с которыми мы сталкиваемся при использовании Date():

  • он поддерживает только UTC и местное время пользователя
  • нет поддержки негригорианских календарей
  • переход на летнее время непредсказуем
  • синтаксический анализ дат из строк ненадежен
  • очень не удобное АПИ
  • Объекты Date изменяемы (mutable): то есть экземпляр объекта Date будет меняться по мере применения методов.

Раньше самой популярной альтернативой была библиотека Moment.js. Однако ее развитие было остановлено и она было переведено в режим обслуживания и теперь не должна использоваться в новых проектах. Они рекомендуют несколько альтернатив, но каждая из них имеет свои плюсы и минусы.

Объект Date() нельзя удалить из JavaScript, но новое АПИ работы со временем находится на этапе 3 в процессе утверждения стандартов TC39.

Объект Temporal

Temporal — это статический глобальный объект верхнего уровня (такой же как Math).

Его основные цели:

  • предсказуемое поведение в разных браузерах и во время выполнения
  • более простое API для вычисления даты и времени
  • поддержка негригорианских календарей
  • поддержка всех часовых поясов, включая арифметику перехода на летнее время
  • парсинг строго определенных строк ISO-8601
  • сделать все экземпляры объектов неизменяемыми

API является всеобъемлющим и может измениться, но вы можете рассчитывать на следующие ключевые функции в какой-то момент в ближайшем будущем.

Текущая дата и время

Temporal.now возвращает текущую дату/время, которые можно передать другим методам для предоставления дополнительной информации. Например:

// exact time since the Unix epoch on 1 Janary, 1970 UTC
Temporal.Now.instant();
Temporal.Now.instant().epochSeconds;
Temporal.Now.instant().epochMilliseconds;

// current time zone
Temporal.Now.timeZone();

// time in current location, e.g.
// 2021-09-18T04:17:48.435068431-04:00[America/New_York]
Temporal.Now.zonedDateTimeISO();

// time in another time zone, e.g.
// 2021-09-18T09:17:48.438068435+01:00[Europe/London]
Temporal.Now.zonedDateTimeISO('Europe/London');

Создание инстанса Даты/Время

Объект Temporal.Instant представляет один момент времени с точностью до наносекунды. Его можно создать из строк в формате ISO 8601 или нескольких секунд, миллисекунд или микросекунд:

const
  t1 = Temporal.Instant.from('2021-03-30T01:45:00+01:00[Europe/Berlin]'),
  t2 = Temporal.Instant.from('2021-04-01T02:00+01:00'),
  t3 = Temporal.Instant.fromEpochSeconds(1.0e8),
  t4 = Temporal.Instant.fromEpochMilliseconds(1.0e10),
  t5 = Temporal.Instant.epochNanoseconds(1.0e12);

Создание даты/времени с учетом тайм зоны

Объект Temporal.ZonedDateTime представляет часовой пояс и дату/время с учетом календаря на момент, когда это произошло (или произойдет) в определенном регионе. Можно использовать самые разные конструкторы:

new Temporal.ZonedDateTime(
  1234567890000, // epoch nanoseconds
  Temporal.TimeZone.from('America/Los_Angeles'), // timezone
  Temporal.Calendar.from('iso8601') // default calendar
);

Temporal.ZonedDateTime.from('2025-12-07T03:24:30+02:00[Africa/Cairo]');

Temporal.Instant('2022-08-05T20:06:13+05:45').toZonedDateTime('+05:45');

Temporal.ZonedDateTime.from({
  timeZone: 'America/New_York'
  year: 2025,
  month: 1,
  day: 7,
  hour: 9,
  minute: 30,
  second: 1,
  millisecond: 2,
  microsecond: 3,
  nanosecond: 4
});

Создание простых дат и времени

Не всегда необходимо использовать точные моменты времени, поэтому Temporal API предоставляет объекты, не зависящие от часовых поясов. Их можно использовать для более простых мероприятий, таких как встреча сегодня в 14:00.

  • Temporal.PlainDateTime относится к календарной дате и времени
  • Temporal.PlainDate относится к определенной календарной дате
  • Temporal.PlainTime относится к определенному времени суток
  • Temporal.PlainYearMonth относится к дате без компонента дня, например «встреча в апреле 2021 года».
  • Temporal.PlainMonthDay относится к дате без компонента года — например, «день Пи — 14 марта».

Все имеют похожие конструкторы:

// create a new PlainDateTime
// both are 4 May 2021 at 1:14pm and 15 seconds
new Temporal.PlainDateTime(2021, 5, 4, 13, 14, 15);
Temporal.PlainDateTime.from('2021-05-04T13:14:15');

// create a new PlainDate
// both are 4 May, 2021
new Temporal.PlainDate(2021, 5, 4);
Temporal.PlainDate.from('2021-05-04');

// create a new PlainTime
// both are 1:14pm and 15 seconds
new Temporal.PlainTime(13, 14, 15);
Temporal.PlainTime.from('13:14:15');

// create a new year PlainYearMonth
// both are April 2021
new Temporal.PlainYearMonth(2021, 4);
Temporal.PlainYearMonth.from('2019-04');

// create a new PlainMonthDay
// both are 14 March
new Temporal.PlainMonthDay(3, 14);
Temporal.PlainMonthDay.from('03-14');

Извлечение значений

Все объекты Temporal могут возвращать дискретные значения об определенной дате/времени. Например, используя ZonedDateTime:

const t1 = Temporal.ZonedDateTime.from('2025-12-07T03:24:30+02:00[Africa/Cairo]');

t1.year;        // 2025
t1.month;       // 12
t1.day;         // 7
t1.hour;        // 3
t1.minute;      // 24
t1.second;      // 30
t1.millisecond; // 0
t1.microsecond; // 0
t1.nanosecond;  // 0

К другим полезным свойствам относятся:

  • dayOfWeek (1 для понедельника и далее до 7 для воскресенье)
  • dayOfYear (от 1 до 365 или 366)
  • weekOfYear (от 1 до 52, или иногда 53)
  • daysInMonth (28, 29, 30, or 31)
  • daysInYear (365 или 366)
  • inLeapYear (true или false)

Сравнение и сортировка дат

Все объекты Temporal  можно сравнивать с помощью функции compare(), которая возвращает целое число. Например, чтобы сравнить два объекта ZonedDateTime:

// returns:
//  -1 if t1 is before t2
//   0 if t1 and t2 are the same
//   1 is t1 is after t2
Temporal.ZonedDateTime.compare(t1, t2);

compare() можно использовать как функцию Array sort() для упорядочения даты/времени в возрастающем хронологическом порядке (от самого раннего к последнему):

const t = [

    '2022-01-01T00:00:00+00:00[Europe/London]',
    '2022-01-01T00:00:00+00:00[Africa/Cairo]',
    '2022-01-01T00:00:00+00:00[America/New_York]'

  ].map( d => Temporal.ZonedDateTime.from(d) )
  .sort( Temporal.ZonedDateTime.compare );

Расчет даты

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

const t1 = Temporal.ZonedDateTime.from('2022-01-01T00:00:00+00:00[Europe/London]');

// add 8 hours 30 minutes
t1.add({ hours: 8, minutes: 30 });

// subtract 5 days
t1.subtract({ days: 5 });

// round to nearest month
t1.round({ smallestUnit: 'month' });

Обратите внимание, что обычные даты Plain  и время могут переноситься. Например, добавление 24 часов к любому PlainTime вернет объект, содержащий идентичное значение.

Методы until() и since() возвращают объект Temporal.Duration, описывающий время до или после определенной даты/времени:

// mounths until t1
t1.until().months;

// weeks since t1
t1.since().weeks;

Наконец, метод equals() может определить, эквивалентны ли два значения даты/времени:

const
  d1 = Temporal.PlainDate.from('2022-01-31');
  d2 = Temporal.PlainDate.from('2021-01-31');

d1.equals(d2); // false

Форматирование дат с помощью API интернационализации

Хотя JavaScript Intl (Internationalization) API не является частью Temporal API, он предоставляет конструктор DateTimeFormat(), который можно использовать для форматирования объектов Temporal или Date:

const d = new Temporal.PlainDate(2021, 3, 14);

// 3/14/2021
new Intl.DateTimeFormat('en-US').format(d);

// 14/3/2021
new Intl.DateTimeFormat('en-GB').format(d);

// miércoles, 14 de abril de 2021
new Intl.DateTimeFormat('es-ES', { dateStyle: 'full' }).format(d);

О времени?

Объект Date() был причиной разочарования разработчиков в течение четверти века. Хотя нет никаких гарантий, что Temporal быстро станет стандартной функцией, будущее дат JavaScript выглядит немного ярче.

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

Больше информации:

Перевод оригинальной статьи: An Introduction to the JavaScript Temporal API

Была ли вам полезна эта статья?
[1 / 5]

Spread the love
Подписаться
Уведомление о
guest
0 Комментарий
Inline Feedbacks
View all comments