@IsDate

Декоратор @IsDate из библиотеки class-validator предназначен для проверки того, что значение является объектом типа Date. Проверка выполняется строго: значение должно быть экземпляром Date, а не строкой, числом или другим представлением даты.

import { IsDate } from 'class-validator'

class UserDto {
  @IsDate()
  createdAt: Date
}

Что проверяет @IsDate

Декоратор валидирует:

  • наличие объекта Date
  • корректность экземпляра через instanceof Date

Успешно проходят проверку:

new Date()
new Date('2025-01-01')

Не проходят проверку:

'2025-01-01'
1715452512
true
{}
[]
null
undefined

Базовый пример

import { validate } from 'class-validator'
import { IsDate } from 'class-validator'

class EventDto {
  @IsDate()
  eventDate: Date
}

async function run() {
  const dto = new EventDto()

  dto.eventDate = new Date()

  const errors = await validate(dto)

  console.log(errors)
}

run()

Результат:

[]

Ошибка при передаче строки

Одна из самых распространённых ошибок — передача даты в виде строки.

class EventDto {
  @IsDate()
  eventDate: Date
}

const dto = new EventDto()

dto.eventDate = '2025-05-10' as any

Результат валидации:

[
  ValidationError {
    property: 'eventDate',
    constraints: {
      isDate: 'eventDate must be a Date instance'
    }
  }
]

Особенность работы с JSON

При получении данных из HTTP-запроса дата почти всегда приходит строкой:

{
  "eventDate": "2025-05-10T12:00:00.000Z"
}

После JSON.parse() значение остаётся строкой:

typeof body.eventDate // string

Из-за этого @IsDate() выдаёт ошибку.


Преобразование строки в Date

Для корректной работы обычно используется библиотека class-transformer.

import { Type } from 'class-transformer'
import { IsDate } from 'class-validator'

class EventDto {
  @Type(() => Date)
  @IsDate()
  eventDate: Date
}

Теперь строка автоматически преобразуется в объект Date.


Использование вместе с plainToInstance

import 'reflect-metadata'
import { plainToInstance } from 'class-transformer'
import { validate } from 'class-validator'

class EventDto {
  @Type(() => Date)
  @IsDate()
  eventDate: Date
}

async function run() {
  const body = {
    eventDate: '2025-05-10T12:00:00.000Z'
  }

  const dto = plainToInstance(EventDto, body)

  console.log(dto.eventDate instanceof Date)

  const errors = await validate(dto)

  console.log(errors)
}

run()

Результат:

true
[]

Проверка невалидной даты

JavaScript позволяет создать некорректный объект Date.

const date = new Date('invalid')

Результат:

Invalid Date

Однако объект всё равно остаётся экземпляром Date.

date instanceof Date // true

Из-за этого возникает важная особенность:

@IsDate()
date: Date

Такой декоратор может пропустить Invalid Date.


Проверка валидности даты

Для полной проверки обычно комбинируют @IsDate и @MinDate / @MaxDate, либо создают собственный валидатор.

Пример проверки через isNaN:

function isValidDate(date: Date) {
  return !isNaN(date.getTime())
}

Комбинация с @MinDate

import { IsDate, MinDate } from 'class-validator'

class EventDto {
  @IsDate()
  @MinDate(new Date())
  eventDate: Date
}

Поле должно:

  1. быть объектом Date
  2. содержать дату не меньше текущей

Комбинация с @MaxDate

import { IsDate, MaxDate } from 'class-validator'

class ArchiveDto {
  @IsDate()
  @MaxDate(new Date())
  createdAt: Date
}

Дата не может быть из будущего.


Проверка диапазона дат

class PromotionDto {
  @IsDate()
  @MinDate(new Date('2025-01-01'))
  @MaxDate(new Date('2025-12-31'))
  activeUntil: Date
}

Пользовательское сообщение об ошибке

class EventDto {
  @IsDate({
    message: 'Дата события имеет неверный формат'
  })
  eventDate: Date
}

Использование функции для сообщения

class EventDto {
  @IsDate({
    message: args => {
      return `Поле ${args.property} должно быть объектом Date`
    }
  })
  eventDate: Date
}

Проверка необязательного поля

Для необязательных свойств используется @IsOptional.

import { IsOptional, IsDate } from 'class-validator'

class UpdateUserDto {
  @IsOptional()
  @IsDate()
  birthday?: Date
}

Поведение:

Значение Результат
undefined проходит
null проходит
new Date() проходит
'2025-01-01' ошибка

Разница между @IsDate и @IsDateString

@IsDate

Проверяет объект Date.

@IsDate()
createdAt: Date

@IsDateString

Проверяет строку формата ISO 8601.

@IsDateString()
createdAt: string

Когда использовать @IsDate

Подходит для:

  • внутренних DTO после трансформации
  • ORM-моделей
  • доменных сущностей
  • сервисного слоя
  • работы с объектами Date

Когда использовать @IsDateString

Подходит для:

  • HTTP API
  • REST-запросов
  • JSON payload
  • внешних клиентов
  • raw-данных до трансформации

Пример сравнения

Входящие данные API

{
  "createdAt": "2025-05-10T10:00:00.000Z"
}

Вариант со строкой

class RequestDto {
  @IsDateString()
  createdAt: string
}

Вариант с трансформацией

class RequestDto {
  @Type(() => Date)
  @IsDate()
  createdAt: Date
}

Использование в NestJS

В NestJS декоратор особенно часто применяется вместе с ValidationPipe.

@Controller('events')
export class EventsController {
  @Post()
  create(@Body() dto: CreateEventDto) {
    return dto
  }
}

DTO:

import { Type } from 'class-transformer'
import { IsDate } from 'class-validator'

export class CreateEventDto {
  @Type(() => Date)
  @IsDate()
  startDate: Date
}

Глобальный pipeline:

app.useGlobalPipes(
  new ValidationPipe({
    transform: true
  })
)

Что делает transform: true

Без трансформации:

startDate = '2025-05-10'

Тип:

string

С трансформацией:

startDate = new Date('2025-05-10')

Тип:

Date

Проверка массива дат

import { IsArray, IsDate } from 'class-validator'
import { Type } from 'class-transformer'

class ScheduleDto {
  @IsArray()
  @Type(() => Date)
  @IsDate({ each: true })
  dates: Date[]
}

Как работает each: true

Опция заставляет валидатор проверять каждый элемент массива отдельно.

dates = [
  new Date(),
  new Date(),
  new Date()
]

Если один элемент невалиден:

dates = [
  new Date(),
  'invalid'
]

валидация завершится ошибкой.


Проверка вложенных объектов

class PeriodDto {
  @Type(() => Date)
  @IsDate()
  start: Date

  @Type(() => Date)
  @IsDate()
  end: Date
}

Использование с ValidateNested

import { ValidateNested } from 'class-validator'
import { Type } from 'class-transformer'

class EventDto {
  @ValidateNested()
  @Type(() => PeriodDto)
  period: PeriodDto
}

Типичные ошибки

Отсутствует @Type(() => Date)

@IsDate()
createdAt: Date

Из JSON всегда придёт строка.


Отсутствует transform: true

В NestJS без трансформации данные не будут преобразованы автоматически.


Использование @IsDate для строки

Неправильно:

@IsDate()
createdAt: string

Правильно:

@IsDateString()
createdAt: string

или

@Type(() => Date)
@IsDate()
createdAt: Date

Проверка nullable-значений

class UserDto {
  @IsOptional()
  @Type(() => Date)
  @IsDate()
  deletedAt: Date | null
}

Кастомный валидатор для корректной даты

import {
  ValidatorConstraint,
  ValidatorConstraintInterface,
  ValidationArguments
} from 'class-validator'

@ValidatorConstraint({ name: 'isRealDate' })
export class IsRealDateConstraint
  implements ValidatorConstraintInterface {

  validate(value: any) {
    return (
      value instanceof Date &&
      !isNaN(value.getTime())
    )
  }

  defaultMessage(args: ValidationArguments) {
    return `${args.property} содержит некорректную дату`
  }
}

Использование:

import { Validate } from 'class-validator'

class EventDto {
  @Validate(IsRealDateConstraint)
  eventDate: Date
}

Проверка даты относительно текущего времени

class MeetingDto {
  @IsDate()
  @MinDate(new Date())
  meetingDate: Date
}

Недостаток такого подхода — дата вычисляется один раз при запуске приложения.


Динамическая проверка даты

Для проверки относительно текущего момента лучше использовать кастомный валидатор.

validate(value: Date) {
  return value.getTime() > Date.now()
}

Проверка временных зон

Date в JavaScript хранит дату в UTC, но отображение зависит от локальной временной зоны.

new Date('2025-05-10')

Может отображаться по-разному на разных серверах.


ISO-формат даты

Наиболее безопасный формат:

2025-05-10T12:00:00.000Z

Он:

  • стандартизирован
  • корректно парсится
  • содержит временную зону
  • предсказуем в API

Совместное использование с Swagger

В Swagger:

@ApiProperty({
  type: String,
  format: 'date-time'
})
@Type(() => Date)
@IsDate()
createdAt: Date

Производительность

@IsDate работает очень быстро, так как использует простую проверку:

value instanceof Date

Даже при большом количестве DTO влияние на производительность минимально.


Внутренний принцип работы

Упрощённая логика:

function isDate(value: unknown): boolean {
  return value instanceof Date
}

Практический пример полноценного DTO

import {
  IsDate,
  IsOptional,
  MinDate,
  MaxDate
} from 'class-validator'

import { Type } from 'class-transformer'

export class CreateBookingDto {
  @Type(() => Date)
  @IsDate()
  @MinDate(new Date())
  checkIn: Date

  @Type(() => Date)
  @IsDate()
  checkOut: Date

  @IsOptional()
  @Type(() => Date)
  @IsDate()
  cancelledAt?: Date
}

Проверка результата трансформации

const dto = plainToInstance(CreateBookingDto, body)

console.log(dto.checkIn)
console.log(typeof dto.checkIn)
console.log(dto.checkIn instanceof Date)

Поддержка TypeScript

@IsDate особенно полезен в связке с:

  • emitDecoratorMetadata
  • reflect-metadata
  • DTO-классами
  • строгой типизацией
  • NestJS
  • class-transformer

Необходимые настройки TypeScript

{
  "experimentalDecorators": true,
  "emitDecoratorMetadata": true
}

Подключение reflect-metadata

Обычно импортируется в точке входа приложения:

import 'reflect-metadata'

Поведение при undefined

class TestDto {
  @IsDate()
  value: Date
}
value = undefined

Результат:

ошибка валидации

Для разрешения undefined нужен @IsOptional().


Поведение при null

value = null

Результат:

ошибка валидации

При использовании @IsOptional():

@IsOptional()
@IsDate()
value?: Date | null

null будет разрешён.


Архитектурная роль @IsDate

Декоратор решает несколько задач одновременно:

  • гарантирует тип даты
  • защищает бизнес-логику
  • предотвращает ошибки сериализации
  • упрощает работу с ORM
  • стандартизирует DTO
  • уменьшает количество ручных проверок
  • делает API предсказуемым