Temporal API & Dayjs
Temporal
- новый API
для работы с датой и временем в JS
.
Обратите внимание: предложение находится на 3 стадии рассмотрения и может подвергнуться некоторым изменениям, так что воздержитесь от его использования в продакшне до официального утверждения (вероятно, это произойдет в конце года).
Сегодня у нас имеется 2 механизма для работы с датой и временем в JS
:
- объект
Date
- конструктор
Intl.DateTimeFormat
Недостатки Date
:
- манипуляции с датой/временем являются сложными;
- поддерживается только
UTC
и время на компьютере пользователя; - поддерживается только григорианский календарь;
- разбор строк в даты подвержен ошибкам;
- объекты
Date
являются мутабельными, т.е. изменяемыми, например:
const today = new Date()
const tomorrow = new Date(today.setDate(today.getDate() + 1))
console.log(tomorrow) // завтрашняя дата
console.log(today) // тоже завтрашняя дата!
Это и многое другое вынуждает разработчиков использовать библиотеки вроде moment.js
(поддержка прекращена) или ее современные альтернативы типа dayjs
или date-fns
при интенсивной работе с датой/временем.
Intl.DateTimeFormat
предназначен исключительно для чувствительного к языку (локали) форматирования даты и времени.
Подробнее о нем и, в целом, об объекте Intl
можно почитать здесь.
Temporal
Вот что можно сделать для того, чтобы поиграть с Temporal
:
- создаем шаблон
JS-проекта
с помощьюcreate-snowpack-app
:
# temporal-test - название проекта
# --template @snowpack/app-template-minimal - используемый шаблон
# --use-yarn - использовать yarn для установки зависимостей
# --no-git - не инициализировать Git-репозиторий
yarn create snowpack-app temporal-test --template @snowpack/app-template-minimal --use-yarn --no-git
# or
npx create-snowpack-app ...
# переходим в директорию
cd temporal-test
# открываем директорию в редакторе кода
# требуется предварительная настройка
code .
- устанавливаем полифил
@js-temporal/polyfill
yarn add @js-temporal/polyfill
# or
npm i @js-temporal/polyfill
- импортируем объекты и расширяем прототип
Date
вindex.js
:
import { Temporal, Intl, toTemporalInstant } from '@js-temporal/polyfill'
Date.prototype.toTemporalInstant = toTemporalInstant
Temporal
предоставляет следующие возможности.
Текущие дата и время
Объект Temporal.Now
возвращает текущие дату и время:
// время (UTC) с начала эпохи, т.е. с 00:00:00 1 января 1970 года
// в секундах
Temporal.Now.instant().epochSeconds
// в миллисекундах
Temporal.Now.instant().epochMilliseconds
// new Date().getTime()
// текущая временная зона
Temporal.Now.timeZone() // Asia/Yekaterinburg
// дата и время в текущей локации с учетом временной зоны
Temporal.Now.zonedDateTimeISO()
// 2022-01-06T13:39:44.178384177+05:00[Asia/Yekaterinburg]
// дата и время в другой временной зоне
Temporal.Now.zonedDateTimeISO('Europe/London')
// 2022-01-06T08:40:22.249422248+00:00[Europe/London]
// дата и время в текущей локации без учета временной зоны
Temporal.Now.plainDateTimeISO()
// 2022-01-06T13:52:19.54213954
// дата в текущей локации
Temporal.Now.plainDateISO()
// время в текущей локации
Temporal.Now.plainTimeISO()
"Мгновенные" дата и время
Объект Temporal.Instant
возвращает объект, представляющий фиксированную позицию во времени с точностью до наносекунд. Позиция (строка) форматируется согласно ISO 8601
следующим образом:
Temporal.Instant.from('2022-03-04T05:06+07:00')
// 2022-03-03T22:06:00Z
// 1 млрд. секун д с начала эпохи
Temporal.Instant.fromEpochSeconds(1.0e9)
// 2001-09-09T01:46:40Z
"Зонированные" дата и время
Объект Temporal.ZonedDateTime
возвращает объект, представляющий фиксированную позицию во времени с точностью до наносекунд с учетом временной зоны и календарной системы:
new Temporal.ZonedDateTime(
123456789000000000n, // наносекунды с начала эпохи (bigint)
Temporal.TimeZone.from('Asia/Yekaterinburg'), // временная зона
Temporal.Calendar.from('iso8601') // дефолтный календарь
) // 1973-11-30T02:33:09+05:00[Asia/Yekaterinburg]
Temporal.ZonedDateTime.from('2025-09-05T02:55:00+01:00[Europe/London]')
// 2025-09-05T02:55:00+01:00[Europe/London]
Temporal.ZonedDateTime.from({
timeZone: 'America/New_York',
year: 2025,
month: 2,
day: 28,
hour: 10,
minute: 15,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 0
}) // 2025-02-28T10:15:00-05:00[America/New_York]
"Обычные" дата и время
Обычные (plain) дата и время возвращают значения (год, месяц, день, час, минута, секунда и т.д.) без учета временной зоны:
Temporal.PlainDate
- дата:
// 2022-01-31
new Temporal.PlainDate(2022, 1, 31)
Temporal.PlainDate.from('2022-01-31')
Temporal.PlainTime
- время:
// 12:00:00
new Temporal.PlainTime(12, 0, 0)
Temporal.PlainTime.from('12:00:00')
Temporal.PlainDateTime
- дата и время:
// 2022-01-31T12:00:00
new Temporal.PlainDateTime(2022, 1, 31, 12, 0, 0)
Temporal.PlainDateTime.from('2022-01-31T12:00:00')
Temporal.PlainYearMonth
- месяц и год:
// июнь 2022 года
// 2022-06
new Temporal.PlainYearMonth(2022, 6)
Temporal.PlainYearMonth.from('2022-06')
Temporal.PlainMonthDay
- месяц и день:
// 4 мая
// 05-04
new Temporal.PlainMonthDay(5, 4)
Temporal.PlainMonthDay.from('05-04')
Значение даты и времени
Объект Temporal
содержит ряд полезных свойств/геттеров:
const date = Temporal.ZonedDateTime.from(
'2022-01-31T12:13:14+05:00[Asia/Yekaterinburg]'
)
date.year // 2022
date.month // 1
date.day // 31
date.hour // 12
date.minute // 13
date.second // 14
date.millisecond // 0
// и т.д.
Другие свойства:
dayOfWeek
- от1
для понедельника до7
для воскресенья;dayOfYear
- от1
до365
или366
в високосный год;weekOfYear
- от1
до52
или53
;daysInMonth
-28
,29
,30
или31
;daysInYear
-365
или366
;inLeapYear
-true
для високосного года.
Сравнение и сортировка даты и времени
Все объекты Temporal
содержат ме тод compare
, который возвращает:
0
, когдаdate1
иdate2
равны;1
, когдаdate1
"больше" (наступит или наступила позже), чемdate2
;-1
, когдаdate1
"меньше" (наступит или наступила раньше), чемdate2
.
const date1 = Temporal.Now.plainDateISO()
const date2 = Temporal.PlainDate.from('2022-04-05')
Temporal.PlainDateTime.compare(date1, date2) // -1
Разумеется, данный метод можно использовать для сортировки:
const sortedDates = [
'2022-01-01T00:00:00[Europe/London]',
'2022-01-01T00:00:00[Asia/Yekaterinburg]',
'2022-01-01T00:00:00[America/New_York]'
]
.map((d) => Temporal.ZonedDateTime.from(d))
.sort(Temporal.ZonedDateTime.compare)
console.log(sortedDates)
/*
[
'2022-01-01T00:00:00+05:00[Asia/Yekaterinburg]',
'2022-01-01T00:00:00+00:00[Europe/London]',
'2022-01-01T00:00:00-05:00[America/New_York]',
]
*/
Вычисление даты и времени
Все объекты Temporal
содержат методы add
, subtract
и round
для продолжительности (duration).
Продолжительность можно определить с помощью объекта Temporal.Duration
, передав ему years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds
, а также знак (sign) -1
для отрицательной и 1
для положительной продолжительности. Вместе с тем, стоит отметить, что указанные методы принимают любые подобные продолжительности (duration-like) значения без необходимости создания специального объекта.
// 2022-01-06
const today = new Temporal.PlainDate(2022, 1, 6)
// прибавляем 1 день
const tomorrow = today.add({ days: 1 })
// или
// прибавляем 2 дня
const dayAfterTomorrow = today.add(Temporal.Duration.from({ days: 2 }))
// вычитаем 1 день
const yesterday = today.subtract({ days: 1 })
console.log(tomorrow) // 2022-01-07
console.log(dayAfterTomorrow) // 2022-01-08
console.log(yesterday) // 2022-01-05
// сегодняшняя дата осталась неизменной
// объекты `Temporal` являются иммутабельными (неизменяемыми)
console.log(today) // 2022-01-06
const duration = Temporal.Duration.from({ days: 2, hours: 12 })
const durationInDays = duration.round({ smallestUnit: 'days' })
// 3 дня
// https://day.js.org/docs/en/durations/as-iso-string
console.log(durationInDays) // P3D