Работа с датами в JavaScript связана с рядом особенностей: неявное приведение типов, различия между строковым и объектным представлением времени, влияние временных зон и неоднозначность форматов. В контексте серверных приложений и DTO-валидации требуется строгий контроль входных значений, где библиотека class-validator предоставляет набор инструментов для проверки и ограничения дат.
В JavaScript дата может существовать в нескольких формах:
Date-объектКаждая форма требует отдельного подхода к валидации, поскольку автоматическое преобразование часто приводит к неоднозначным результатам:
"2026-01-01" — валидная ISO-строка, но интерпретация
зависит от контекста"01/02/2026" — неоднозначный формат (день/месяц или
месяц/день)Date может быть Invalid Date, не вызывая
явной ошибки при созданииВ class-validator основной инструмент для проверки объекта
Date:
import { IsDate } from 'class-validator';
export class CreateEventDto {
@IsDate()
startDate: Date;
}
Декоратор @IsDate() проверяет, что значение является
экземпляром Date и при этом не является
Invalid Date.
Особенность: строковые значения автоматически не преобразуются в
Date. Без предварительной трансформации валидация не
пройдет.
Для корректной работы с входными JSON-данными часто применяется связка с class-transformer:
import { Type } from 'class-transformer';
import { IsDate } from 'class-validator';
export class CreateEventDto {
@Type(() => Date)
@IsDate()
startDate: Date;
}
Декоратор @Type(() => Date) обеспечивает
преобразование входной строки в объект Date до этапа
валидации.
Типичная схема обработки входных данных:
В случаях, когда дата приходит в виде строки, используется проверка формата ISO:
import { IsISO8601 } from 'class-validator';
export class CreateEventDto {
@IsISO8601()
startDate: string;
}
Особенности:
DateДопустимые примеры:
2026-05-15T10:00:00Z2026-05-152026-05-15T10:00:00+03:00Недопустимые примеры:
15-05-20262026/05/15May 15 2026Для ограничения нижней границы используется
@MinDate():
import { MinDate } from 'class-validator';
export class CreateEventDto {
@Type(() => Date)
@MinDate(new Date('2026-01-01'))
startDate: Date;
}
Поведение:
getTime())Особенности применения:
Пример динамической границы:
@MinDate(new Date(Date.now()))
startDate: Date;
Однако такое использование может привести к неконсистентности при повторных вызовах, поскольку дата вычисляется один раз при загрузке модуля.
Для верхней границы применяется @MaxDate():
import { MaxDate } from 'class-validator';
export class CreateEventDto {
@Type(() => Date)
@MaxDate(new Date('2030-01-01'))
startDate: Date;
}
Используется в сценариях:
Сравнение двух дат внутри одной модели напрямую не поддерживается встроенными декораторами, поэтому применяется кастомная логика:
import { registerDecorator, ValidationArguments } from 'class-validator';
export function IsAfter(property: string) {
return function (object: Object, propertyName: string) {
registerDecorator({
name: 'isAfter',
target: object.constructor,
propertyName,
constraints: [property],
validator: {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
if (!value || !relatedValue) return false;
return new Date(value).getTime() > new Date(relatedValue).getTime();
},
},
});
};
}
Применение:
export class CreateEventDto {
@Type(() => Date)
startDate: Date;
@Type(() => Date)
@IsAfter('startDate')
endDate: Date;
}
Часто даты приходят в виде чисел (Unix timestamp):
import { IsNumber } from 'class-validator';
export class CreateEventDto {
@IsNumber()
startTimestamp: number;
}
Дополнительная проверка корректности временного диапазона выполняется вручную:
startTimestamp > 0 && startTimestamp < 4102444800000
(ограничение примерно до 2100 года в миллисекундах)
При преобразовании строк в Date возможна ситуация
Invalid Date. Проверка через class-validator:
import { ValidateBy, ValidationOptions } from 'class-validator';
export function IsValidDate(validationOptions?: ValidationOptions) {
return ValidateBy(
{
name: 'isValidDate',
validator: {
validate(value: any) {
return value instanceof Date && !isNaN(value.getTime());
},
},
},
validationOptions,
);
}
Использование:
export class CreateEventDto {
@Type(() => Date)
@IsValidDate()
startDate: Date;
}
Date в JavaScript всегда хранится в UTC, однако строковые представления могут содержать локальные смещения:
2026-05-15T10:00:00Z — UTC2026-05-15T10:00:00+03:00 — смещение +3 часаПри валидации важно учитывать:
@IsDate()
startDate: Date;
При передаче строки в JSON валидация всегда будет провалена без
@Type(() => Date).
Строки вида 01-02-2026 интерпретируются по-разному в
различных средах исполнения.
if (dto.startDate < dto.endDate)
При строковом типе сравнение становится лексикографическим, а не временным.
Практическая схема для строгой проверки даты включает несколько уровней:
export class CreateEventDto {
@Type(() => Date)
@IsDate()
@MinDate(new Date())
@MaxDate(new Date('2030-01-01'))
startDate: Date;
}
Такой подход обеспечивает:
В типичных серверных приложениях DTO с датами формируют основу контрактов между слоями:
Строгая валидация дат снижает риск ошибок, связанных с интерпретацией времени, и обеспечивает консистентность данных на уровне бизнес-логики.