Temporal JavaScript: Дата и Время
Обработка дат и времени в JavaScript уже давно вызывает недовольство разработчиков. Встроенный объект Date, созданный на заре существования JavaScript, имеет множество ограничений и особенностей, которые усложняют работу с датами и временем.
К счастью для нас, предложение Temporal направлено на решение этих проблем путем предоставления современного, более интуитивно понятного API для манипулирования датой и временем.
В этой статье мы рассмотрим некоторые проблемы, связанные с работой с date, рассмотрим, что такое Temporal API и как он будет работать, а также то, что вы можете использовать до тех пор, пока Temporal не будет готов к использованию в рабочей среде.
Текущие проблемы с датой в JavaScript
1. Изменяемый API
Объект Date является изменяемым, что может привести к ошибкам и неожиданному поведению:
// Current Date behavior is mutable
const date = new Date('January 1, 2024');
date.setMonth(1); // Modifies the original date object
console.log(date); // February 1, 2024
// This mutability can lead to bugs when passing dates between functions
function processDate(date) {
date.setDate(date.getDate() + 1); // Modifies the original date!return date;
}
2. Запутанная нумерация месяцев
Месяцы в дате отсчитываются от нуля (0-11), а дни - от единицы (1-31).:
// Confusing month numbering
const date = new Date(2024, 0, 1); // January 1, 2024
console.log(date.getMonth()); // 0 (January)
3. Ограниченная поддержка часовых поясов
Объект Date имеет ограниченную поддержку часовых поясов и в значительной степени зависит от местного часового пояса системы:
// Time zone handling is system-dependent
const date = new Date('2024-01-01T00:00:00Z');
console.log(date.toString()); // Will show different results based on system timezone
Что такое Temporal API?
Temporal - это предлагаемый новый JavaScript API, который предоставляет современное решение для работы с датами, временем и временными метками. В настоящее время это предложение находится на стадии 3, что означает, что оно находится на завершающей стадии разработки, но еще не готово для использования в производственной среде.
Ключевые понятия Temporal:
- Неизменяемый по умолчанию: Все временные объекты неизменяемы
- Четкое разделение задач: Разные объекты для разных вариантов использования
- Четкая обработка часовых поясов:
- Улучшенная поддержка работы с часовыми поясами
- Согласованная индексация: Все единицы измерения нумеруются на основе 1
Ключевые особенности Temporal
1. Различные типы для различных нужд
// PlainDate for working with calendar dates
const date = Temporal.PlainDate.from('2024-01-01');
// PlainTime for working with wall-clock time
const time = Temporal.PlainTime.from('09:00:00');
// ZonedDateTime for working with specific time zones
const zonedDateTime = Temporal.ZonedDateTime.from('2024-01-01T09:00:00[America/New_York]');
В этом примере Temporal предоставляет разные типы объектов для разных вариантов использования:
- PlainDate используется, когда вас интересуют только календарные даты без указания времени или часового пояса. Идеально подходит для дней рождения, праздников и т.д. PlainTime определяет время независимо от даты или часового пояса. Полезно для повторяющихся событий, таких как "ежедневный подъем в 9 утра". ZonedDateTime объединяет информацию о дате, времени и часовом поясе для полной обработки временных меток. Отлично подходит для планирования совещаний в разных часовых поясах.
Каждый тип создан специально и неизменяем, что предотвращает случайные изменения. Это четкое разделение помогает разработчикам выбрать подходящий инструмент для их конкретных нужд, в отличие от универсального объекта Date, который пытается справиться со всем и часто приводит к путанице.
2. Неизменяемые операции
// All operations return new objects instead of modifying the original
const date = Temporal.PlainDate.from('2024-01-01');
const nextMonth = date.add({ months: 1 });
console.log(date.toString()); // '2024-01-01' - original unchanged
console.log(nextMonth.toString()); // '2024-02-01' - new object
Этот пример демонстрирует, как неизменяемый дизайн Temporal предотвращает случайные изменения и делает арифметику дат более предсказуемой.
В API current Date такие методы, как setMonth(), изменяют исходный объект, что может привести к ошибкам при использовании этого объекта в нескольких местах. В отличие от этого, методы Temporal всегда возвращают новые объекты, оставляя исходный нетронутым.
3. Улучшенная поддержка часовых поясов
// Explicit time zone handling
const nyDateTime = Temporal.ZonedDateTime.from({
timeZone: 'America/New_York',
year: 2024,
month: 1,
day: 1,
hour: 9
});
const tokyoDateTime = nyDateTime.withTimeZone('Asia/Tokyo');
console.log(tokyoDateTime.toString()); // '2024-01-01T23:00:00+09:00[Asia/Tokyo]'
В отличие от текущего Date API, который часто приводит к путанице с неявными преобразованиями часовых поясов, Temporal делает операции с часовыми поясами явными и простыми:
- Мы создаем объект ZonedDateTime специально для часового пояса Нью-Йорка, в котором четко указаны все компоненты (год, месяц, день, час). Это явное создание предотвращает любую двусмысленность в отношении того, с каким часовым поясом мы работаем. Используя функцию withTimeZone(), мы можем легко переводить время между поясами без сложных вычислений. Перевод времени из Нью-Йорка в Токио выполняется автоматически. Результирующая строка вывода включает полное смещение часового пояса (+09:00) и название часового пояса ([Азия/Токио]), что обеспечивает полную ясность в отношении отображаемого времени.
Этот подход решает многие распространенные проблемы, связанные с часовыми поясами, с которыми сталкиваются разработчики сегодня, такие как переход на летнее время, расчеты смещения часовых поясов и неоднозначность местного времени по сравнению с UTC. Это особенно ценно для приложений, которым необходимо выполнять глобальное планирование, координацию событий в разных часовых поясах или в любом другом сценарии, где точное управление часовыми поясами имеет решающее значение.
Сравнение даты, международного и временного значений
Текущий подход с указанием даты и международных данных:
// Current approach using Date and Intl
const date = new Date('2024-01-01T09:00:00Z');
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: 'America/New_York',
dateStyle: 'full',
timeStyle: 'long'
});
console.log(formatter.format(date)); // 'Monday, January 1, 2024 at 4:00:00 AM EST'
При нынешнем подходе мы создаем временную метку UTC, используя Date, нам нужен отдельный Intl.Объект DateTimeFormat для форматирования, мы неявно обрабатываем преобразование часовых поясов во время форматирования и имеем меньший контроль над точным форматом вывода. Итоговый результат показывает 4:00 утра по восточному времени, потому что мы задали дату как 09:00 UTC и отформатировали ее в часовом поясе Нью-Йорка. Это неявное преобразование может привести к путанице и ошибкам.
Будущий подход с учетом временных:
// Future approach using Temporal
const datetime = Temporal.ZonedDateTime.from('2024-01-01T09:00:00[America/New_York]');
console.log(datetime.toLocaleString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'short'
})); // 'Monday, January 1, 2024 at 9:00:00 AM EST'
С помощью Temporal мы создаем ZonedDateTime с явным указанием часового пояса, а параметры форматирования напрямую интегрированы в API. Время (9:00 утра) - это именно то, что мы указали для Нью-Йорка, без каких-либо неявных преобразований. Это делает поведение кода более предсказуемым и понятным.
При таком подходе арифметика также становится более интуитивно понятной.
const nextWeek = datetime.add({ weeks: 1 });
const duration = datetime.until(nextWeek);
console.log(nextWeek.toPlainDate().toString()); // '2024-01-08'
console.log(duration.toString()); // 'PT168H'
В этом примере мы можем увидеть несколько ключевых преимуществ подхода Temporal:
Явная обработка часовых поясов: создавая ZonedDateTime с помощью [America/New_York], мы явно указываем, с каким часовым поясом мы работаем.
Нет никакой двусмысленности в отношении того, является ли время UTC, местным или в другом часовом поясе. Интегрированное форматирование: метод toLocaleString() предоставляет простой и унифицированный способ форматирования дат без использования отдельного объекта formatter.
Все параметры форматирования аналогичны тем, которые вы использовали бы в Intl.DateTimeFormat, что обеспечивает удобство работы и упрощает API. Интуитивно понятная арифметика: Методы add() и until() демонстрируют, как Temporal упрощает вычисления даты и времени: add({ недели: 1 }) ясно показывает, что мы добавляем одну неделю функция until() возвращает правильный объект duration, который можно легко понять и которым можно манипулировать Результирующая длительность "PT168H" представляет собой период времени (P), равный 168 часам (T168H), в соответствии с форматом продолжительности ISO 8601
Безопасность типов: благодаря наличию различных типов, таких как ZonedDateTime и PlainDate, Temporal помогает предотвратить распространенные ошибки. Метод toPlainDate() явно преобразует представление только даты, когда нам не нужна информация о времени.
Такой подход устраняет многие ошибки и неявное поведение, которые затрудняют работу current Date API, обеспечивая при этом более мощный и гибкий способ работы с датами и временем.