Валидация временных значений в схемах Joi строится вокруг работы с
типом Date, при этом часто возникает необходимость
принимать и обрабатывать числовые представления времени. На практике
используются два основных формата: Unix timestamp в секундах и
JavaScript timestamp в миллисекундах. Их различие критично для
корректной валидации входных данных и предотвращения логических
ошибок.
Unix timestamp (секунды) представляет собой количество секунд, прошедших с 1 января 1970 года (UTC). Этот формат широко используется в API, базах данных и внешних сервисах.
JavaScript timestamp (миллисекунды) — это количество
миллисекунд с той же эпохи. Именно в таком виде работает встроенный
объект Date.now() и большинство методов Date в
JavaScript.
Ключевое различие:
17153300001715330000000Разница в масштабировании составляет фактор 1000, и это
является частой причиной ошибок при интеграции систем.
В Joi поддержка временных меток реализована через метод
timestamp(), который позволяет валидировать числовые
значения как дату.
Базовая сигнатура:
Joi.date().timestamp([type])
Параметр type определяет формат входного timestamp.
Режим 'javascript' ожидает значение в миллисекундах.
import Joi from 'joi';
const schema = Joi.object({
createdAt: Joi.date().timestamp('javascript')
});
schema.validate({
createdAt: 1715330000000
});
Поведение:
DateРежим 'unix' работает с секундной точностью.
const schema = Joi.object({
createdAt: Joi.date().timestamp('unix')
});
schema.validate({
createdAt: 1715330000
});
Особенности:
1000 внутри JoiJoi допускает неявное приведение типов, если включена соответствующая конфигурация:
const schema = Joi.object({
createdAt: Joi.date().timestamp('unix')
});
schema.validate({
createdAt: "1715330000"
});
В этом случае строка будет преобразована в число перед валидацией.
Однако поведение зависит от настроек convert:
Joi.object({...}).validate(data, { convert: true });
При convert: false любые строковые значения будут
считаться невалидными.
После успешной валидации Joi всегда возвращает объект
Date, независимо от входного формата:
const { value } = schema.validate({
createdAt: 1715330000
});
console.log(value.createdAt instanceof Date); // true
Это важно учитывать при дальнейшем использовании данных, так как исходное числовое значение теряется.
При работе с javascript timestamp проблем с точностью
обычно не возникает, так как JavaScript использует миллисекунды как
базовую единицу времени.
В режиме unix возможны нюансы:
seconds → milliseconds всегда
линейноеJoi позволяет комбинировать timestamp() с другими
методами проверки:
const schema = Joi.object({
createdAt: Joi.date()
.timestamp('unix')
.min('now')
.max('now')
});
Такое использование позволяет ограничивать временные границы относительно текущего момента.
Примеры логики:
min('now') — только будущие датыmax('now') — только прошедшие датыПри интеграции с внешними сервисами часто возникает ситуация, когда формат времени неизвестен заранее. В таких случаях применяются альтернативные стратегии:
const schema = Joi.alternatives().try(
Joi.date().timestamp('unix'),
Joi.date().timestamp('javascript')
);
Это позволяет принимать оба формата без предварительного преобразования.
Наиболее распространённые ошибки:
// ошибка: передан seconds вместо milliseconds
createdAt: 1715330000
В режиме 'javascript' это приведёт к дате в 1970
году.
createdAt: "not-a-timestamp"
При отключённом convert значение будет отклонено.
Timestamp всегда интерпретируется как UTC-время, поэтому любые локальные часовые пояса игнорируются на этапе валидации.
Joi позволяет сочетать timestamp с ISO-строками:
const schema = Joi.object({
createdAt: Joi.alternatives().try(
Joi.date().timestamp('unix'),
Joi.date().iso()
)
});
Такой подход часто используется в API, где формат входных данных не стандартизирован.
После валидации объект Date может быть сериализован в
JSON:
JSON.stringify({ date: new Date() });
Результат:
Для сохранения числового формата требуется явное преобразование:
date.getTime() // milliseconds
Math.floor(date.getTime() / 1000) // seconds
Использование 'unix' предпочтительно в следующих
случаях:
Использование 'javascript' оправдано:
Date.now()Несовпадение форматов без явного контроля приводит к ошибкам смещения времени на порядок величины (×1000), что является одной из наиболее критичных проблем при работе с временными метками в распределённых системах.