YourWorldTime

ISO 8601 и как записать дату однозначно

· 4 мин чтения

ISO 8601 существует потому, что 03/04/2026 — это март в Америке и апрель во всём остальном мире. Запись 2026-04-03 снимает двусмысленность, правильно сортируется как текст и есть то, что должен принимать любой интерфейс.

03/04/2026 — это 3 апреля почти во всём мире и 4 марта в Соединённых Штатах. По одной лишь строке понять, что имелось в виду, невозможно, и оба прочтения совершенно разумны. Каждый год эта двусмысленность порождает сорванные сроки, двойные брони и хотя бы одну памятную аварию.

ISO 8601 — международный стандарт, который её устраняет.

Основной формат

2026-04-03T14:30:00Z

Слева направо, от старшей единицы к младшей:

  • 2026-04-03 — год, месяц, день, каждый с ведущим нулём, всегда в этом порядке
  • T — разделитель даты и времени, обязательный в строгой форме
  • 14:30:00 — часы, минуты, секунды по 24-часовым часам
  • Z — обозначение пояса. Z означает ровно UTC

Дело именно в порядке. Поскольку составляющие идут от самой значимой к наименее значимой, строка ISO 8601 правильно сортируется как обычный текст. При простом сравнении строк 2026-04-03 идёт раньше 2026-04-10, чего не удаётся ни одному другому распространённому формату. Одно это свойство оправдывает стандарт.

Обозначения пояса

Годятся три формы, и значат они разное:

Форма Значение
2026-04-03T14:30:00Z 14:30 UTC. Однозначно
2026-04-03T14:30:00+05:45 14:30 местного времени в поясе, опережающем UTC на 5 ч 45 мин. Однозначно
2026-04-03T14:30:00 14:30 в неуказанном поясе. Не метка времени

Беду приносит третья форма. Это местные дата и время — показание часов без всякой возможности разместить его на оси времени. Передать такое через границу системы и надеяться, что другая сторона предположит тот же пояс, что и вы, — это ошибка, ждущая развёртывания в другом регионе.

Заметьте также, чем смещение не является: +05:45 сообщает смещение Непала, а не то, что метка времени из Непала. Смещения — не пояса: одно и то же смещение делят несколько поясов, а смещение пояса меняется. Ведите идентификатор рядом, если нужно знать где.

RFC 3339 — профиль, который вам, скорее всего, и нужен

ISO 8601 огромен. Он допускает недельные даты (2026-W14-5), порядковые даты (2026-093), дробные единицы и усечённую форму без века. Почти ничего из этого в интерфейсе вам не нужно.

RFC 3339 — ужатый профиль для интернет-протоколов. Он требует полной даты, требует обозначения пояса и допускает в качестве разделителя либо T, либо пробел. Когда применительно к интерфейсу говорят «формат ISO», почти всегда имеют в виду RFC 3339.

Полезное правило: принимайте всё, что переварит хороший разборщик; выдавайте только строгий RFC 3339 с Z.

Длительности и интервалы

Используются реже, и знать их стоит на случай встречи.

Длительности начинаются с P от «период», а T отделяет часть даты от части времени:

  • P3Y6M4D — три года, шесть месяцев, четыре дня
  • PT1H30M — час тридцать минут
  • P1DT12H — сутки двенадцать часов

T не факультативна: P1M — это месяц, PT1M — минута. Это классическая ошибка с длительностями ISO 8601.

Интервалы соединяют две точки косой чертой: 2026-04-03T14:00Z/2026-04-03T16:00Z — либо точку и длительность: 2026-04-03T14:00Z/PT2H.

Структурированные данные HowTo на этом сайте используют синтаксис длительностей — PT2M для двухминутного шага, — потому что schema.org предписывает длительности ISO 8601.

Недельные даты и ловушка внутри них

2026-W14-5 — это пятница четырнадцатой недели ISO 2026 года. Недели ISO начинаются с понедельника, а неделя 1 — та, что содержит первый четверг января.

Следствие, на котором спотыкаются: год недели ISO не всегда совпадает с календарным годом. 1 января 2027 года попадает в 53-ю неделю ISO 2026 года. Код форматирования, соединяющий номер недели ISO с календарным годом, каждый год на несколько дней даёт неверную дату — а в большинстве языков спецификаторы формата выглядят почти одинаково (YYYY против GGGG или %Y против %G).

Практические правила

  1. Храните в UTC, выдавайте с Z. У 2026-04-03T14:30:00Z везде ровно одно значение.
  2. Никогда не выдавайте голые местные дату и время через границу. Если пояс не приложить, у вас нет метки времени.
  3. Дополняйте нулями всё. 2026-4-3 — не годный ISO 8601, хотя большинство разборщиков его примет.
  4. Используйте разделители - и :. Компактная форма (20260403T143000Z) законна, но недружелюбна, и часть разборщиков её отвергает.
  5. Ведите идентификатор пояса отдельно, когда нужно знать где, а не только какое смещение. Europe/London переживёт смену правил, +01:00 — нет.

Подводные камни разбора

Даже при наличии стандарта именно разбор оказывается местом, где всё ломается.

Конструктор Date в JavaScript непоследователен для входа не по стандарту. new Date('2026-04-03') разбирается как полночь UTC. new Date('2026-04-03T00:00:00') — без обозначения пояса — разбирается как местная полночь. Разница между ними равна вашему смещению, а значит, один и тот же код даёт разные даты в зависимости от места запуска. Всегда указывайте обозначение пояса.

Двузначные годы двусмысленны, и их следует отвергать. ISO 8601 допускает усечённую форму; принимать её не должно почти ничто. 26-04-03 может быть 1926 или 2026 годом, а скользящее окно, которое это решает, — политическое решение, спрятанное в разборщике.

Доли секунды бывают разной точности. 14:30:00.5, 14:30:00.500 и 14:30:00.500000 одинаково допустимы и обозначают один и тот же момент. Разборщик, сравнивающий строки вместо моментов, с этим не согласится.

Запятая законна. ISO 8601 допускает 14:30:00,5 — десятичную запятую — и даже предпочитает её. Большинство разборщиков её отвергает. Если вы принимаете данные из европейской системы, будьте готовы.

У полуночи два написания. 24:00:00 одного дня — тот же момент, что 00:00:00 следующего, и обе записи допустимы. Выдавайте вторую форму, принимайте обе.

Замечание о сортировке

Сортируемость ISO 8601 верна только для строк с одинаковым представлением пояса. 2026-04-03T14:00:00Z и 2026-04-03T10:00:00-05:00 — один и тот же момент, но как строки вторая идёт раньше.

Если вы сортируете метки времени как текст — в журнале, в имени файла, в текстовом столбце базы, — сначала приведите их все к UTC с Z. Иначе порядок окажется свойством форматирования, а не времени.