ISO форматы

Формат ISO 8601 представляет собой международный стандарт представления дат и времени. Он используется для унификации временных значений в API, базах данных и межсервисном взаимодействии. В контексте JavaScript и серверной валидации особое значение имеет корректная проверка строк, содержащих даты в этом формате, поскольку некорректные значения приводят к ошибкам сериализации, неверной сортировке и логическим сбоям в бизнес-логике.

Валидация ISO-строк в библиотеке Joi строится вокруг строгих правил распознавания формата, а также возможности преобразования строки в объект Date для дальнейшей работы.


Поддержка ISO-форматов в Joi

Библиотека Joi предоставляет несколько механизмов для работы с ISO-датами, зависящих от типа схемы:

  • Joi.string().isoDate() — проверка строки на соответствие ISO 8601
  • Joi.date().iso() — строгая проверка даты в ISO-формате с преобразованием
  • комбинирование с .strict(), .raw() и .custom() для расширенной логики

Основное различие между строковой и датовой валидацией заключается в том, что строковая проверка оставляет значение в исходном виде, тогда как Joi.date() выполняет парсинг.


Строгая проверка ISO-строк

При использовании строковой схемы:

import Joi from 'joi';

const schema = Joi.object({
  createdAt: Joi.string().isoDate()
});

В данном случае допустимыми значениями считаются строки, соответствующие стандарту ISO 8601:

  • 2026-05-10
  • 2026-05-10T14:30:00Z
  • 2026-05-10T14:30:00+06:00

Любые отклонения, включая нестандартные разделители или локальные форматы, приводят к ошибке валидации.

Особенность isoDate() заключается в том, что проверяется именно структура строки, без преобразования в объект Date.


Работа через Joi.date().iso()

Более строгий и функциональный подход основан на типе date:

const schema = Joi.object({
  createdAt: Joi.date().iso()
});

В этом случае происходит:

  • парсинг строки в объект Date
  • проверка соответствия ISO 8601
  • нормализация временной зоны
  • возможность дальнейших операций с датой

Входные данные могут быть как строкой, так и Date, однако при несоответствии формату выбрасывается ошибка.


Различия между string().isoDate() и date().iso()

Ключевые различия можно описать через поведение:

Поведение string().isoDate() date().iso()
Проверка формата Да Да
Преобразование в Date Нет Да
Сохранение исходного значения Да Нет
Поддержка операций с датой Нет Да

Выбор подхода зависит от архитектуры системы. Валидация на уровне строк применяется в API-слоях, тогда как date().iso() используется в доменной логике.


Поддерживаемые ISO-варианты

ISO 8601 включает несколько форматов, которые Joi способен распознавать:

Дата без времени

2026-05-10

Полный формат с временем

2026-05-10T14:30:00Z

С указанием смещения

2026-05-10T14:30:00+06:00

С миллисекундами

2026-05-10T14:30:00.123Z

Все эти варианты считаются валидными при использовании .isoDate() или .date().iso().


Временные зоны и нормализация

При использовании Joi.date().iso() происходит автоматическая нормализация временной зоны в объекте Date. Это означает:

  • входная строка с +06:00 преобразуется в UTC-время
  • внутреннее хранение всегда основано на Unix timestamp
  • при сериализации возможны различия отображения

Важно учитывать, что Date в JavaScript не хранит исходную временную зону, что может приводить к потере контекста.


Ошибки валидации ISO

Типовые причины отклонения значений:

  • отсутствие разделителя T
  • использование пробелов вместо T
  • неверный формат месяца или дня
  • отсутствие временной зоны при строгих правилах
  • использование локальных форматов (10.05.2026)

Пример невалидного значения:

2026/05/10 14:30

Комбинация ISO-валидации с другими правилами

Joi позволяет расширять ISO-проверки дополнительными ограничениями:

const schema = Joi.object({
  createdAt: Joi.date().iso().min('2020-01-01').max('now')
});

В данном случае добавляются ограничения диапазона дат, что позволяет контролировать не только формат, но и бизнес-логику временных значений.


Использование кастомных правил поверх ISO

При необходимости расширенной логики применяется .custom():

const schema = Joi.object({
  createdAt: Joi.date().iso().custom((value, helpers) => {
    if (value.getUTCHours() === 0) {
      return helpers.error('date.invalidMidnight');
    }
    return value;
  })
});

Такая конструкция позволяет накладывать дополнительные ограничения на уже валидированный ISO-формат.


Особенности сериализации и передачи данных

При передаче данных через JSON ISO-строки являются стандартом де-факто:

{
  "createdAt": "2026-05-10T14:30:00Z"
}

После валидации через Joi.date().iso() значение может быть преобразовано в Date, что требует внимательности при последующей сериализации, так как формат может измениться.


Производственные сценарии использования ISO-валидации

ISO-валидация чаще всего применяется в следующих случаях:

  • API REST и GraphQL
  • события в очередях сообщений
  • логирование временных меток
  • синхронизация между микросервисами
  • хранение временных данных в базах

Стандартизация через ISO 8601 позволяет избежать проблем локализации и неоднозначности форматов.


Ограничения и нюансы поведения Joi

При работе с ISO-форматами следует учитывать:

  • различия между Node.js версиями в парсинге дат
  • поведение временных зон при сериализации
  • неоднозначность значений без явного Z или offset
  • потерю исходного формата при использовании Date

Эти аспекты влияют на архитектурные решения при проектировании схем валидации.