x

Добро пожаловать в IT Leader Assistant.
Please Войти!

Создать аккаунт

Temporal API vs Date. Сравнение двух JavaScript API для работы с датами

Александра Шаламова
07-15-2021 12:29
Temporal API vs Date. Сравнение двух JavaScript API для работы с датами
Новое Temporal API для работы с датами в JavaScript прошло третью стадию технического комитете TC39 на добавление в стандарт ECMA. Оно может быть добавлено в ближайших релизах языка. Давайте посмотрим какие отличия нас ждут от Date и как с помощью Temporal API можно решить старые задачи по-новому.

Как попробовать новое Temporal API

Чтобы запустить новое Temporal API есть три основных метода:
  • самый простой это зайти на сайт документации и на любой из страниц открыть консоль браузера. Temporal API будет уже встроено в консоль.
  • на странице GitHub предложения для Temporal API есть полифил, который можно скачать и запустить в специальном playground
  • поставить npm пакет и импортировать его у себя в коде.
Для примеров из этой статьи я использую консоль браузера Chrome на странице документации.

Создание экземпляра

Для создания значения Date в текущей реализации языка используется следующие форматы:
new Date(milliseconds);
new Date(dateString);
new Date(year, month[, day[, hour[, minute[, second[, millisecond]]]]]);
В Temporal API вы также можете создать дату из строки:
const instant = Temporal.Instant.from('1969-07-20T20:17Z');
А вот определение значения даты через ее составляющие будет отличатся. В Temporal в метод from передается объект, описывающий дату. В отличии, от Date, где значения принимает конструктор, в порядке, который необходимо помнить, в объекте принимаемом методом from более наглядно и в любом порядке можно указать все необходимые значения:
const zonedDateTime = Temporal.ZonedDateTime.from({
  timeZone: 'America/Los_Angeles',
  year: 1995,
  month: 12,
  day: 7,
  hour: 3,
  minute: 24,
  second: 30,
  millisecond: 0,
  microsecond: 3,
  nanosecond: 500
});
У Temporal API уже на этапе создания даты есть свои преимущества. Передача параметров в формате объекта оберегает от случайных ошибок, позволяет не думать о порядке следования значений, при этом в коде более наглядно видно какое значение, в какой параметр передается.

Взятие основных значений

Следующее отличие, которое мы рассмотрим это взятие основных свойств из даты, таких как день, месяц, год и т.д.
У экземпляра объекта Date представлен набор методов get, таких как getDate, getFullYear и так далее для получения всех необходимых значений.
const Xmas = new Date('December 25, 1995 23:15:30');
console.log(Xmas.getDate()); // 25
console.log(Xmas.getMonth()); // 11
console.log(Xmas.getFullYear()); // 1995
В новой реализации все эти значения получаются не с помощью методов, а как свойства объекта с соответствующим названием day, month, year и т.д.:
const myDate = new Temporal.ZonedDateTime.from({ timeZone: 'Europe/Moscow', year: 2021, month: 7, day: 14 });

console.log(myDate.day); // 14
console.log(myDate.dayOfWeek); // 3
console.log(myDate.month); // 7
console.log(myDate.year); // 2021
При этом отметим, что в Temporal отсчет месяца идет с 1, а в Date с 0, поэтому номер месяца будет соответствовать номеру текущего месяца без необходимость прибавлять единицу, как при использовании Date. Важно помнить об этой разнице при использовании нового API. Также вместо метода getFullYear, в котором многие путались, теперь просто свойство year.
В целом видно, что привели в порядок недоработки Date, о которых приходилось помнить и обходить в коде. Взятие значений стало более очевидным и интуитивно понятным, что конечно приведет к меньшему числу случайных ошибок и более низкому порогу входа в работу с датами.

Автокоррекция

Еще одной фичей, которая использовалась в Date, но работает иначе в Temporal API является автокоррекция даты. Если вы передадите в Date какое-то значение больше возможного, но дата автоматически пересчитается, с учетом этого излишка. На пример, если вы укажете двенадцатый месяц, вместо 11 (например, если забыли учесть, что месяцы начинаются с нуля, как мы обсуждали выше), то вместо декабря текущего года, вы получите январь следующего:
const myDate = new Date(2020, 12, 1);
console.log(myDate) // Fri Jan 01 2021 00:00:00 GMT+0300 (Moscow Standard Time)
В отличии от этого поведения, Temporal API округлит значение, которое превышает возможное к ближайшему возможному:
const myDate = new Temporal.ZonedDateTime.from({
    timeZone: 'Europe/Moscow', 
    year: 2020, 
    month: 13, 
    day: 101
});
console.log(myDate.toString()); // 2020-12-31T00:00:00+03:00[Europe/Moscow]
Кто умело пользовался возможностью автокоррекции, возможно расстроится такому поведению, однако это может предотвратить множество случайных и сложноотлавливаемых багов и улучшит читаемость кода.

Проверка является ли год високосным

В возможностях Date нет реализации для проверки года на високосность. Однако это можно сделать несколькими способами, такими как проверка количества дней в году и количества дней в феврале, например:
const isLeap = year => new Date(year, 1, 29).getDate() === 29;
console.log(isLeap(2020)); // true
console.log(isLeap(2021)); // false
В Temporal API есть специальное свойство для проверки является ли год високосным:
const myDate = new Temporal.ZonedDateTime.from({ timeZone: 'Europe/Moscow', year: 2020, month: 13, day: 1 });
console.log(myDate.inLeapYear); // true
Конечно это функционал, который не так часто нужен, но все же упрощает код и делает его более простым и читаемым.

Прибавление и вычитание N дней / месяцев / лет к дате

Чтобы добавить какое-то количество дней, месяцев или лет к дате с помощью Date нам нужно установить соответствующее значение, модифицируя текущее значение. Соответственно, если вам нужно, например, прибавить три года четыре месяца и шесть дней, то нужно будет делать три разных операции взятия текущих значений и три операции присвоения нового значения. Для наглядности посмотрим пример:
const date = new Date();
date.setMonth(date.getMonth() + 4);
console.log(date.toString()); // Sun Nov 14 2021 16:46:09 GMT+0300 (Moscow Standard Time) 

date.setFullYear(date.getFullYear() + 3);
console.log(date.toString()); // Thu Nov 14 2024 16:46:09 GMT+0300 (Moscow Standard Time)

date.setDate(date.getDate() + 6)
console.log(date.toString()); // Wed Nov 20 2024 16:46:09 GMT+0300 (Moscow Standard Time)
Еще одним методом является прибавление к дате соответствующего количества миллисекунд, что упрощает реализацию, но вводит кучу магических чисел в код, которые по-хорошему нужно выносить в константы, при этом нужно учесть сколько в последующих месяцах дней, не високосный ли это год и так далее. В итоге вы получите еще более громоздкое решение, реализуя которое не сложно ошибиться.
В Temporal API есть специальные методы для прибавления и вычитания, в которых, с помощью объекта указываются все параметры добавления. Только в результате возвращается новый экземпляр даты, а не меняется текущий. Попробуем сделать тоже самое, что в примеры выше, но с использование метода add из Temporal API:
let myDate = Temporal.now.zonedDateTimeISO();
myDate = myDate.add({ years: 3, months: 4, days: 6 });
console.log(myDate.toString()); // 2024-11-20T00:00:00+03:00[Europe/Moscow]
В итоге мы получаем очень легкое для запоминания, короткое решение, которое ни раз пригодится каждому.

Работа с таймзонами

По умолчанию дата через конструктор Date создается в текущей таймзоне браузера. Вы можете отобразить время в другой таймзоне с помощью метода toLocaleString, например:
(new Date()).toLocaleString("en-US", {timeZone: 'Europe/Paris'}) // "7/14/2021, 4:11:50 PM"
Также вы можете работать со значениями времени по всемирному координированному времени через UTC-методы. Например вы можете брать и изменять значения по координированному времени и дата в вашей таймзоне будет меняться:
const date = new Date();
console.log(date); // Wed Jul 14 2021 17:14:54 GMT+0300 (Moscow Standard Time)

console.log(date.getUTCHours()) // 14
console.log(date.getHours()) // 17

date.setUTCHours(10)
console.log(date.getUTCHours()) // 10
console.log(date.getHours()) // 13
С помощью этих методов, вы можете работать с датой без привязки к таймзоне. Однако если вам нужно перевести дату в другую таймзону, то легко это сделать не получится. Вы можете например создать метод, который будет создавать новую дату, передавая ей строкой дату с другой таймзоной:
function convertTZ(date, timeZone) {
    return new Date((typeof date === "string" ? new Date(date) : date).toLocaleString("en-US", {timeZone: timeZone}));   
}
const myDate = new Date()
console.log(myDate); // Wed Jul 14 2021 17:25:26 GMT+0300 (Moscow Standard Time)

const newDate = convertTZ(myDate, 'America/Los_Angeles')
console.log(newDate2);  // Wed Jul 14 2021 07:25:26 GMT+0300 (Moscow Standard Time)
Как видим время поменялось на то, которое должно быть в переданной таймзоне, но на деле ваша дата остается все равно в вашей текущей таймзоне, у меня это “GMT+0300 (Moscow Standard Time)”.
Что же меняет Temporal API в работе с таймзонами? Ну во-первых, он дает нам полноценную возможность создавать даты с указание таймзоны и работать с этими таймзонами с помощью Temporal.ZonedDateTime:
const parisDate = new Temporal.ZonedDateTime.from({ timeZone: 'Europe/Paris', year: 2020, month: 7, day: 14 });
console.log(parisDate.toString()) // 2020-07-14T00:00:00+02:00[Europe/Paris]
И при этом таймзона даты действительно та, что вы задали и вы даже можете ее поменять и работать именно с датой в нужной вам таймзоне:
const losDate = parisDate.withTimeZone('America/Los_Angeles');
console.log(losDate.toString()); // 2020-07-13T15:00:00-07:00[America/Los_Angeles]
Если же вам таймзоны вообще не нужны, то в Temporal есть специальный объект для работы с датами без таймзоны:
const dateTime = Temporal.PlainDateTime.from({
  year: 1995,
  month: 12,
  day: 7,
  hour: 15
});

console.log(dateTime.toString()); // 1995-12-07T15:00:00
В плане таймзон в Temporal API действительно сделан огромный шаг вперед. Вы конечно и раньше могли бы подключить какую-нибудь библиотеку, где реализован функционал таймзон, которых реализовано в избытке. Однако библиотека это всегда лишний код, другой синтаксис, который должны знать все члены команды, проблема поддержки этой библиотеки со временем и так далее. Конечно для большинства это все мелочи, но зачем идти даже на них, если можно прямо в языке иметь полноценное API для работы с таймзонами и использовать его.

Заключение

Конечно же это далеко не все отличия Temporal API от Date в JavaScript. Я выбрала лишь те, что лично мне показались самыми интересными и наглядными, чтобы показать, что новое API действительно переработанная версия и новый подход к работе с датами. Больше подробностей читайте в официальной документации. Если у вас есть какие-то комментарии, то обязательно делитесь ими внизу статьи, я все обязательно прочитаю и каждому отвечу. Возможно вы знаете, как что-то можно было бы сделать лучше. А если хотите продолжения с другими сравнениями, тоже пишите об этом в комментариях и я обязательно продолжу эту тему.
Если у вас есть какие-то вопросы или пожелания вы можете воспользоваться формой для связи с нами:

Комментарии

Чтобы оставить комментарий, пожалуйста, авторизуйтесь

Подписывайтесь на обновления

Последние статьи из нашего блога