Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Temporal API в JavaScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Temporal API це нове вбудоване API для роботи з датами й часом у JavaScript, яке приходить на заміну проблемному `Date`.** Воно дає незмінні (immutable) об'єкти та чітке розділення понять: `Temporal.Instant` (точка на шкалі UTC), `PlainDate` / `PlainTime` / `PlainDateTime` (локальні значення без зони), `ZonedDateTime` (дата + час + часовий пояс з бази IANA), `Duration` (тривалість) і `Temporal.Now` (поточний час). Арифметика не мутує вихідний об'єкт, часовий пояс зберігається як частина значення, а некоректна дата дає `RangeError` замість тихого «переносу» на наступний місяць. ```javascript const today = Temporal.PlainDate.from('2025-10-16'); const tomorrow = today.add({ days: 1 }); // новий об'єкт console.log(today.toString(), tomorrow.toString()); // 2025-10-16 2025-10-17 ``` **Ключове:** `Date` мутує, плутає локальний час з UTC і мовчки виправляє помилки; Temporal незмінний, явний щодо часового поясу і суворий до некоректних значень.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Temporal API це новий стандартний набір типів для роботи з датами й часом у JavaScript, який має замінити `Date`.** Це пропозиція TC39, що дійшла до Stage 3, вже реалізована в частині рушіїв і доступна скрізь через офіційний polyfill. Головна ідея: незмінні об'єкти, чітке розділення понять «момент», «дата», «час» і «часовий пояс», плюс точна робота з базою часових поясів IANA. ## Теорія ### TL;DR - `Temporal` це нове вбудоване API дат і часу; воно не розширює `Date`, а повністю його замінює. - Усі об'єкти **незмінні**: `add()`, `subtract()`, `with()`, `round()` повертають нове значення. - Окремі типи під окремі задачі: `Instant`, `PlainDate`, `PlainTime`, `PlainDateTime`, `ZonedDateTime`, `Duration`, `Now`. - Часовий пояс зберігається як частина значення (`[Europe/Kyiv]`), а не лише як зміщення `+03:00`, тому переходи на літній та зимовий час обробляються правильно. - Некоректна дата дає `RangeError` (з `overflow: 'reject'`) або явний clamp замість тихого «переносу», як у `new Date(2025, 1, 30)`. - Місяці нумеруються з **1**, а не з 0, як у `Date`. - Форматування виводу віддається `Intl.DateTimeFormat`; у старих середовищах підключають `@js-temporal/polyfill`. ### Швидкий приклад ```javascript // Дата без часу const date = Temporal.PlainDate.from('2025-10-16'); console.log(date.year, date.month, date.day); // 2025 10 16 // Час без дати const time = Temporal.PlainTime.from('13:45:30'); console.log(time.hour); // 13 // Дата й час без часового поясу const dt = Temporal.PlainDateTime.from('2025-10-16T13:45:30'); console.log(dt.toString()); // 2025-10-16T13:45:30 ``` ### Чому `Date` не вистачає `Date` це одна з найстаріших і найпроблемніших частин мови: його API скопіювали з Java ще у 1995 році й фактично не змінювали. | Проблема | Приклад | | --- | --- | | Неявні часові пояси | `new Date('2024-01-01')` парситься як UTC, а `new Date('2024-01-01T00:00')` як локальний час | | Непередбачувані обчислення | `new Date(2024, 1, 30)` дає 1 березня, бо лютого 30 не існує | | Мутуючі методи | `setMonth()`, `setHours()` змінюють вихідний об'єкт, а не повертають новий | | Незручна робота з UTC | доводиться вручну жонглювати `toISOString()` і парою `getHours()` / `getUTCHours()` | | Нумерація з нуля | місяці 0..11, а дні місяця 1..31 | | Немає типів для календарів, тривалостей і зон | за будь-якою дрібницею тягнули сторонню бібліотеку: Moment, date-fns, Luxon | Саме тому Temporal не «лагодить» `Date`, а вводить окрему, послідовну модель часу. ### Класи Temporal | Клас | Що представляє | Приклад | | --- | --- | --- | | `Temporal.Instant` | момент на абсолютній шкалі часу (точка в UTC) | `"2025-10-16T21:00:00Z"` | | `Temporal.PlainDate` | дата без часу | `"2025-10-16"` | | `Temporal.PlainTime` | час без дати | `"21:00:00"` | | `Temporal.PlainDateTime` | дата й час без часового поясу | `"2025-10-16T21:00:00"` | | `Temporal.ZonedDateTime` | дата + час + часовий пояс | `"2025-10-16T21:00:00+03:00[Europe/Kyiv]"` | | `Temporal.Duration` | тривалість, різниця між моментами | `"P3DT5H"` (3 дні 5 годин) | | `Temporal.Now` | утиліта доступу до поточного часу | `Temporal.Now.instant()` | Різниця між `PlainDateTime` і `ZonedDateTime` ключова на співбесіді. `PlainDateTime` це «настінний» час без прив'язки до місця: «16 жовтня о 21:00» може означати різні миті в різних країнах. `ZonedDateTime` містить зону, тому однозначно вказує на конкретну мить і знає про переходи DST. Коли вам потрібна саме мить (мітка події, запис у лог), беріть `Instant` або `ZonedDateTime`; коли потрібен «день народження» чи «час відкриття офісу», беріть `PlainDate` / `PlainTime`. ### Часові пояси та перехід на літній час ```javascript const zdt = Temporal.ZonedDateTime.from({ timeZone: 'Europe/Kyiv', year: 2025, month: 10, day: 16, hour: 21, }); console.log(zdt.toString()); // 2025-10-16T21:00:00+03:00[Europe/Kyiv] console.log(zdt.toInstant().toString()); // 2025-10-16T18:00:00Z (те саме в UTC) ``` Temporal зберігає ідентифікатор зони (`[Europe/Kyiv]`) як частину даних, а не лише зміщення `+03:00`. Завдяки цьому арифметика враховує переходи на літній та зимовий час: ```javascript const beforeDST = Temporal.ZonedDateTime.from('2025-03-30T01:30:00+01:00[Europe/Berlin]'); const afterDST = beforeDST.add({ hours: 1 }); console.log(afterDST.toString()); // 2025-03-30T03:30:00+02:00[Europe/Berlin] ``` Годинник «перестрибнув» з 01:30 на 03:30, бо о 02:00 стрілки перевели вперед: додали одну реальну годину, і зміщення змінилося з `+01:00` на `+02:00`. Поточний час беруть через `Temporal.Now`: ```javascript const now = Temporal.Now.instant(); console.log(now.toString()); // поточний час у UTC const kyiv = Temporal.Now.zonedDateTimeISO('Europe/Kyiv'); console.log(kyiv.toString()); // наприклад: 2025-10-16T23:50:00+03:00[Europe/Kyiv] ``` ### Обчислення, Duration і сувора валідація Усі операції повертають нові значення, вихідний об'єкт залишається недоторканим: ```javascript const today = Temporal.PlainDate.from('2025-10-16'); const tomorrow = today.add({ days: 1 }); const lastWeek = today.subtract({ weeks: 1 }); console.log(tomorrow.toString()); // 2025-10-17 console.log(lastWeek.toString()); // 2025-10-09 console.log(today.toString()); // 2025-10-16, не змінився ``` Різниця між двома моментами це `Temporal.Duration` в ISO 8601 форматі: ```javascript const start = Temporal.PlainDateTime.from('2025-10-16T10:00'); const end = Temporal.PlainDateTime.from('2025-10-18T15:30'); const duration = end.since(start); console.log(duration.toString()); // P2DT5H30M (2 дні 5 годин 30 хвилин) console.log(duration.days, duration.hours, duration.minutes); // 2 5 30 ``` Некоректні значення не «виправляються» тихцем. Рядок з неіснуючою датою одразу кидає помилку, а для об'єкта поведінку задає опція `overflow`: ```javascript // Старий Date мовчки переносить дату new Date(2025, 1, 30); // 2025-03-02 (місяць 1 це лютий, 30 лютого не існує) // Temporal: рядок з неіснуючою датою одразу помилка Temporal.PlainDate.from('2025-02-30'); // RangeError // Для об'єкта за замовчуванням overflow: 'constrain' (обрізає до межі місяця) Temporal.PlainDate.from({ year: 2025, month: 2, day: 30 }); // 2025-02-28 // Суворий режим Temporal.PlainDate.from({ year: 2025, month: 2, day: 30 }, { overflow: 'reject' }); // RangeError ``` Зверніть увагу: у Temporal `month: 2` це лютий, бо місяці нумеруються з 1. У `Date` та сама позиція означала б березень. ### Переваги, форматування та підтримка | Перевага | Що дає | | --- | --- | | Незмінність | безпечні операції без мутацій і без випадкових побічних ефектів | | Чітка модель | розділення понять «момент», «дата», «час», «зона» | | Точні часові пояси | база IANA, коректний DST, зона всередині значення | | Немає «магії» `Date` | помилки замість неявних коригувань | | Сумісність з `Intl` | форматування для користувача без ручного склеювання рядків | | ISO 8601 і `Duration` | проста робота з різницями та інтервалами | Форматування віддано `Intl.DateTimeFormat`, тому локалізація виглядає звично: ```javascript const zdt = Temporal.ZonedDateTime.from('2025-10-16T21:00:00+03:00[Europe/Kyiv]'); const fmt = new Intl.DateTimeFormat('uk-UA', { dateStyle: 'full', timeStyle: 'long' }); console.log(fmt.format(zdt)); // четвер, 16 жовтня 2025 р. о 21:00:00 за східноєвропейським літнім часом ``` Де вже можна користуватися: - Найновіші рушії постачають `Temporal` нативно (першим повну реалізацію включив Firefox), у решти вона в роботі. - У Node.js і старих браузерах підключають офіційний polyfill. - Перед використанням у проді перевіряйте підтримку на MDN або через `typeof Temporal !== 'undefined'`. ```bash npm i @js-temporal/polyfill ``` ```javascript import { Temporal } from '@js-temporal/polyfill'; const date = Temporal.PlainDate.from('2025-10-16'); console.log(date.toString()); // 2025-10-16 ``` ### Типові помилки - **Очікувати мутації.** `dt.add({ days: 1 })` нічого не змінює в `dt`; результат треба присвоїти. Це найчастіша помилка для тих, хто звик до `setDate()`. - **Плутати `PlainDateTime` і `ZonedDateTime`.** `PlainDateTime` не є миттю на шкалі часу: без зони його не можна коректно порівняти з `Instant` чи зберегти як мітку події. - **Зберігати лише зміщення.** `+03:00` це не часовий пояс: наступного тижня після переходу на зимовий час те саме місце вже матиме `+02:00`. Зберігайте ідентифікатор IANA (`Europe/Kyiv`). - **Нумерація місяців з нуля.** Перенісши код з `Date`, легко залишити `month: 0`; у Temporal це `RangeError`, бо січень це `1`. - **Розраховувати на `RangeError` там, де діє `constrain`.** `from({...})` за замовчуванням обрізає значення до межі місяця; щоб отримати помилку, передайте `{ overflow: 'reject' }`. - **Змішувати `Date` і `Temporal` у розрахунках.** Конвертуйте явно: `date.toTemporalInstant()` та `instant.epochMilliseconds`. - **Тягнути важку бібліотеку задля `Temporal`.** Сам polyfill теж важить чимало, тож для одного форматування іноді достатньо `Intl.DateTimeFormat` над звичайним `Date`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.