"now" в правилах dateВ Joi для описания временных ограничений используется
специализированный синтаксис, позволяющий ссылаться на момент выполнения
валидации. Ключевое значение — строка "now",
интерпретируемая как текущий момент времени в системе.
import Joi from 'joi';
const schema = Joi.object({
publishedAt: Joi.date().less('now')
});
В этом случае значение publishedAt должно быть строго
меньше текущей даты и времени в момент вызова validate.
Проверка выполняется динамически, а не при создании схемы.
Важно учитывать, что "now" не является фиксированным
значением. Оно вычисляется каждый раз при валидации, что делает
поведение схемы чувствительным ко времени выполнения.
less, greater,
min, maxJoi предоставляет набор методов для сравнения дат относительно текущего времени.
less('now')Ограничивает дату значением строго меньше текущего времени.
Joi.date().less('now')
Используется для:
greater('now')Ограничивает дату значением строго больше текущего времени.
Joi.date().greater('now')
Применяется для:
max('now') и
min('now')Эти методы работают аналогично, но включают границу.
Joi.date().max('now')
Joi.date().min('now')
max('now') — допускает значение вплоть до текущего
момента включительно;min('now') — допускает текущий момент и всё, что
позже."now"При передаче строки "now" библиотека интерпретирует её
как вызов Date.now() во время выполнения валидации. Это
означает:
Пример:
const schema = Joi.date().less('now');
schema.validate(new Date(Date.now() - 1000)); // валидно
schema.validate(new Date(Date.now() + 1000)); // ошибка
Использование "now" создаёт эффект
недетерминированности. В тестах это может приводить к нестабильным
результатам.
Проблема проявляется при сравнении с границей времени:
const schema = Joi.date().less('now');
const result = schema.validate(new Date());
В зависимости от момента выполнения теста значение может оказаться валидным или нет.
Для устранения неопределённости часто используют явное вычисление текущего времени:
const now = new Date();
const schema = Joi.object({
expiresAt: Joi.date().greater(now)
});
Здесь момент времени фиксируется один раз и становится стабильной точкой отсчёта.
Joi.ref и динамических значенийХотя Joi.ref чаще применяется для ссылок внутри объекта,
его можно использовать для передачи значений, связанных с текущим
временем, через внешний слой.
const now = Date.now();
const schema = Joi.object({
createdAt: Joi.date().max(new Date(now))
});
Это позволяет контролировать точку отсчёта вне схемы и делает её воспроизводимой.
При более сложных сценариях используется .custom, где
разработчик получает полный контроль над сравнением.
const schema = Joi.date().custom((value, helpers) => {
const now = Date.now();
if (value.getTime() > now) {
return helpers.error('date.future');
}
return value;
});
Преимущества такого подхода:
При необходимости точного контроля времени часто подключаются внешние библиотеки:
dayjsmoment (устаревает, но встречается в старых
проектах)date-fnsПример с dayjs:
import dayjs from 'dayjs';
const schema = Joi.date().custom((value, helpers) => {
if (dayjs(value).isAfter(dayjs())) {
return helpers.error('date.future');
}
return value;
});
Такой подход полезен при работе с часовыми поясами и сложными временными интервалами.
Joi автоматически нормализует входные значения:
"2026-05-10T12:00:00Z")DateВсе они приводятся к внутреннему представлению Date.
Joi.date().less('now')
будет корректно работать со всеми этими форматами.
Однако стоит учитывать, что:
Date объект зависит от окружения исполнения.При сравнении с текущим временем возможны микрорасхождения:
Date и его проверкой;Пример:
const value = new Date();
setTimeout(() => {
schema.validate(value);
}, 10);
Даже небольшая задержка может изменить результат при строгих
сравнениях less('now') или greater('now').
Комбинация с when позволяет строить зависимые
правила:
const schema = Joi.object({
startDate: Joi.date(),
endDate: Joi.date().when('startDate', {
is: Joi.date().less('now'),
then: Joi.date().greater(Joi.ref('startDate')),
otherwise: Joi.date().greater('now')
})
});
Здесь логика зависит одновременно от:
Каждый вызов validate пересчитывает "now"
заново:
const schema = Joi.date().greater('now');
const value = new Date(Date.now() + 1000);
schema.validate(value); // может быть валидно
setTimeout(() => schema.validate(value), 2000); // может стать невалидно
Это критично при работе с кэшированными данными или повторной проверкой объектов.
При использовании в распределённых системах важно учитывать:
Валидация "now" всегда опирается на локальное время
среды выполнения, что делает поведение зависимым от инфраструктуры.
Для стабильных систем применяются следующие подходы:
now через контекст (DI);"now" в критичных
доменах.Пример централизованного времени:
const getNow = () => new Date();
const schema = Joi.date().less(getNow());
Такой подход упрощает тестирование и повышает предсказуемость поведения схем.