Який формат для часових міток у JSON
Головне: Epoch-числа й рядки ISO мають власні компроміси, але ламає речі саме їх змішування. Використовуйте ISO 8601 зі зміщенням, зберігайте UTC, відображайте в часовому поясі користувача.

Коротко: використовуйте ISO 8601 з явним зміщенням у мережі, зберігайте UTC із завершальним Z і конвертуйте лише під час показу; якщо обираєте epoch-числа, глобально фіксуйте секунди проти мілісекунд і кладіть одиницю в назву поля.
Чому 2026-10-30 14:30:00 недостатньо
Цей рядок не несе поясу. 14:30 у Шанхаї й 14:30 у Лондоні різняться на вісім годин, і немає способу сказати, який мається на увазі. new Date('2026-10-30 14:30:00') розбирається в локальному поясі середовища виконання, тож сервер і браузер виводять два різні моменти з одного навантаження — баг, що ніколи не відтворюється.
Гола 2026-10-30 взагалі не є моментом. Добре для днів народження й розрахункових періодів, неправильно для часу створення.
Два робочі формати
Рядки ISO 8601
{
"createdAt": "2026-10-30T14:30:00+08:00",
"updatedAt": "2026-10-30T06:30:00Z"
}
Читабельні, сортовані за узгодженого формату, з точністю до мілісекунд за потреби й самодостатні.
Epoch-числа
{ "createdAt": 1793519400 }
Компактні й однозначні, але нечитабельні, і секунди проти мілісекунд — постійна небезпека. Якщо обираєте epoch, стандартизуйте одиницю й кладіть її в назву поля: createdAtMs набагато безпечніше за createdAt.
Конвенції, варті раннього закріплення
| Пункт | Конвенція |
|---|---|
| Формат у мережі | ISO 8601 з Z або +08:00 |
| Пояс зберігання | UTC, щоб літній час ніколи не входив у картину |
| Точність | Мілісекунд достатньо |
| Найменування полів | createdAt, updatedAt, expiresAt |
| Чисті дати | Окреме поле date, наприклад birthday: "1990-01-01" |
| Тривалості | Ціле плюс одиниця, наприклад timeoutMs: 3000 |
Відображення
new Intl.DateTimeFormat('en-GB', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Europe/London',
}).format(new Date('2026-10-30T06:30:00Z'));
Вказуйте timeZone явно, коли показ не повинен слідувати за пристроєм. Покладання на типове значення браузера правильне для часу для користувача й неправильне для звітів.
Три повторювані баги
Голі дати розбираються як опівночі UTC. За специфікацією ECMAScript рядки лише з датою є UTC, тоді як рядки дати-часу є локальними. Ця неузгодженість — надійне джерело багів на один день.
Пояс бази даних розходиться з серіалізатором. datetime у MySQL не має поясу; пишіть локально, читайте як UTC — і все зсувається на зміщення.
Літній час. Збереження майбутніх локальних часів створює моменти, що не існують або трапляються двічі. В UTC таких прогалин немає.
Часті питання
Як обирати між ISO 8601 і epoch-числами?
Використовуйте ISO 8601, коли люди читають навантаження, коли хочете лексикографічний порядок сортування або коли значення має нести власний пояс; ціна — приблизно десяток зайвих байтів і бібліотека розбору. Використовуйте epoch-числа, коли компактність і тривіальна міжмовна обробка важливіші, але глобально фіксуйте секунди проти мілісекунд і кажіть, яку одиницю містить поле.
Чи мають гола дата й часова мітка використовувати те саме поле?
Ні. Гола 2026-10-30 — це календарний день без часу й без поясу, тоді як поле часової мітки має нести точний момент. Тримайте дні народження й розрахункові періоди в окремому полі date, а для часу створення чи оновлення використовуйте поле ISO 8601 At.