Работа с датами в валидационной библиотеке Yup строится вокруг
объекта Yup.date(), который предоставляет набор методов для
проверки, ограничения и сравнения значений типа Date. При
разработке форм и бизнес-логики часто возникает необходимость сравнивать
даты между собой: проверять хронологический порядок, ограничивать
диапазоны, задавать зависимость одной даты от другой.
В Yup все операции сравнения дат опираются на внутреннее приведение
значений к типу Date. Это означает, что строковые
представления дат (например, ISO-формат) автоматически преобразуются
перед проверкой.
Основные методы сравнения:
min(date, message?) — минимально допустимая датаmax(date, message?) — максимально допустимая датаtest(name, message, fn) — произвольная логика
сравненияНаиболее простой способ сравнения дат — использование диапазонов.
import * as Yup from 'yup';
const schema = Yup.object({
startDate: Yup.date()
.min(new Date(2024, 0, 1), 'Дата не может быть раньше 01.01.2024')
});
Метод min проверяет, что значение не меньше указанной
границы. Внутри происходит сравнение временных меток
(getTime()).
const schema = Yup.object({
endDate: Yup.date()
.max(new Date(2026, 11, 31), 'Дата не может быть позже конца 2026 года')
});
Метод max работает симметрично min,
ограничивая верхнюю границу диапазона.
Частая задача — проверка, что одна дата следует после другой. Например, дата окончания должна быть позже даты начала.
refYup позволяет ссылаться на другие поля схемы через
Yup.ref.
const schema = Yup.object({
startDate: Yup.date().required(),
endDate: Yup.date()
.min(Yup.ref('startDate'), 'Дата окончания должна быть позже начала')
});
Здесь происходит динамическое сравнение: значение
endDate проверяется относительно значения
startDate в момент валидации.
Иногда требуется, чтобы даты не только соблюдали порядок, но и не были равны.
const schema = Yup.object({
startDate: Yup.date().required(),
endDate: Yup.date()
.min(Yup.ref('startDate'), 'Недопустимый диапазон')
.test(
'not-equal',
'Даты не должны совпадать',
function (value) {
const { startDate } = this.parent;
if (!value || !startDate) return true;
return value.getTime() !== new Date(startDate).getTime();
}
)
});
Здесь используется test, так как встроенные методы
min и max не различают строгое и нестрогое
сравнение.
Для более точного контроля используются пользовательские тесты.
endDate: Yup.date().test(
'is-after',
'Дата должна быть строго позже',
function (value) {
const { startDate } = this.parent;
if (!value || !startDate) return true;
return value.getTime() > new Date(startDate).getTime();
}
)
startDate: Yup.date().test(
'is-before',
'Дата должна быть раньше',
function (value) {
const { endDate } = this.parent;
if (!value || !endDate) return true;
return value.getTime() < new Date(endDate).getTime();
}
)
Сравнение часто используется для задания интервалов. Yup позволяет
комбинировать min и max.
const schema = Yup.object({
eventDate: Yup.date()
.min(new Date(2025, 0, 1))
.max(new Date(2025, 11, 31))
});
Такой подход задаёт фиксированный временной промежуток.
refГораздо более гибкий вариант — зависимость от других полей.
const schema = Yup.object({
minDate: Yup.date().required(),
maxDate: Yup.date()
.min(Yup.ref('minDate'), 'Максимальная дата не может быть раньше минимальной')
});
Здесь maxDate динамически зависит от
minDate, что позволяет строить каскадные ограничения.
Yup выполняет сравнение только после приведения типов. При
необходимости можно явно управлять этим процессом через
transform.
const schema = Yup.object({
date: Yup.date().transform((value, originalValue) => {
return originalValue === '' ? null : value;
})
});
Это важно при сравнении, так как некорректное преобразование может
привести к Invalid Date, что нарушает логику
min и max.
Распространённый кейс — проверка относительно текущего времени.
const schema = Yup.object({
birthDate: Yup.date()
.max(new Date(), 'Дата не может быть в будущем')
});
Или наоборот:
const schema = Yup.object({
eventDate: Yup.date()
.min(new Date(), 'Дата не может быть в прошлом')
});
Сравнение в Yup основано на getTime(), что включает
миллисекунды. Это может приводить к неожиданным результатам, если одна
дата содержит время, а другая нет.
Пример проблемы:
2026-01-01T00:00:00.000Z2026-01-01T12:00:00.000ZНесмотря на одинаковый день, сравнение даст различие.
Для устранения таких ситуаций используется нормализация:
function normalize(date) {
const d = new Date(date);
d.setHours(0, 0, 0, 0);
return d;
}
И применение в test:
test('same-day', 'Даты должны быть в один день', function (value) {
const { otherDate } = this.parent;
if (!value || !otherDate) return true;
return normalize(value).getTime() === normalize(otherDate).getTime();
});
Сложные схемы часто требуют объединения нескольких проверок:
const schema = Yup.object({
startDate: Yup.date().required(),
endDate: Yup.date()
.required()
.min(Yup.ref('startDate'))
.test('max-range', 'Интервал слишком большой', function (value) {
const { startDate } = this.parent;
if (!value || !startDate) return true;
const diff = value - new Date(startDate);
const days = diff / (1000 * 60 * 60 * 24);
return days <= 30;
})
});
Здесь реализуется одновременно:
null и undefinedПри сравнении дат Yup игнорирует проверку min и
max, если значение отсутствует, если не указано
required().
Yup.date().min(new Date()) // null пройдет проверку
Yup.date().required().min(new Date()) // null не пройдет
Это важно учитывать при построении зависимых схем.
Внутренняя логика сравнения дат в Yup опирается на три механизма:
min, max)Yup.ref)test)Их комбинация позволяет строить как простые временные ограничения, так и сложные бизнес-правила, зависящие от нескольких дат одновременно.