Валидация объектов Date

Работа с датами в схемах Zod опирается на строгую типизацию объекта Date и его корректную валидацию через встроенный примитив z.date().


Схема z.date() принимает только валидные экземпляры Date.

import { z } from "zod";

const schema = z.date();

schema.parse(new Date()); // корректно
schema.parse("2024-01-01"); // ошибка
schema.parse(Date.now()); // ошибка

Ключевое поведение:

  • допускается только объект Date
  • исключаются строки, числа, timestamp
  • проверяется валидность даты (не Invalid Date)
schema.parse(new Date("invalid")); // ошибка: Invalid Date

Проверка валидности Date и внутренние ограничения

Объект Date в JavaScript может существовать в невалидном состоянии:

new Date("not-a-date") // Invalid Date

Zod дополнительно проверяет корректность:

const schema = z.date();

schema.parse(new Date("not-a-date"));
// ошибка валидации

Таким образом, недостаточно только типа Date, важна его семантическая корректность.


z.coerce.date(): приведение строк и чисел к Date

Во многих входных данных даты приходят в виде строк или timestamp. Для этого используется z.coerce.date().

const schema = z.coerce.date();

schema.parse("2024-01-01"); // Date
schema.parse(1704067200000); // Date

Поддерживаемые форматы:

  • ISO-строки
  • timestamp (число)
  • строки, распознаваемые Date.parse

Особенность:

  • происходит автоматическое преобразование до проверки

Ограничение диапазона дат: min и max

Zod предоставляет встроенные методы ограничения диапазона:

const schema = z.date().min(new Date("2020-01-01"));
const schema = z.date().max(new Date("2030-01-01"));

Комбинирование:

const schema = z.date().min(new Date("2020-01-01")).max(new Date("2030-01-01"));

Поведение:

  • дата вне диапазона вызывает ошибку валидации
  • сравнение происходит по timestamp

Проверка через refine для сложной логики

Для нестандартных условий применяется refine.

const schema = z.date().refine((date) => {
  const year = date.getFullYear();
  return year % 4 === 0;
}, {
  message: "Год должен быть високосным"
});

Типичные сценарии:

  • проверка рабочих дней
  • ограничение по времени суток
  • бизнес-логика (например, только будние даты)

Работа с optional и nullable датами

optional

const schema = z.date().optional();

Допустимые значения:

  • Date
  • undefined

nullable

const schema = z.date().nullable();

Допустимые значения:

  • Date
  • null

комбинация

const schema = z.date().optional().nullable();

Преобразование даты через transform

Zod позволяет трансформировать дату в другой формат.

const schema = z.date().transform((date) => date.toISOString());

Результат:

  • вход: Date
  • выход: string

Частые преобразования:

  • ISO строка
  • timestamp
  • пользовательский формат
const schema = z.date().transform((d) => ({
  year: d.getFullYear(),
  month: d.getMonth() + 1,
  day: d.getDate()
}));

Парсинг ISO строк без coerce

Без coerce строка всегда считается ошибкой:

z.date().parse("2024-01-01"); // ошибка

Корректная обработка:

z.coerce.date().parse("2024-01-01"); // Date

Safe parsing и обработка ошибок

Метод safeParse позволяет получить результат без исключений.

const result = z.date().safeParse("invalid");

if (!result.success) {
  console.log(result.error);
}

Структура ошибки включает:

  • путь ошибки
  • описание причины
  • тип несоответствия

Особенности работы с timestamp

Числовые значения интерпретируются только через coercion:

z.coerce.date().parse(0); // 1970-01-01

Без coercion:

z.date().parse(0); // ошибка

Валидация времени и временных зон

JavaScript Date всегда хранит время в UTC-формате, но отображение зависит от локали.

const schema = z.coerce.date();

schema.parse("2024-01-01T10:00:00Z");

Особенности:

  • Zod не выполняет нормализацию часовых поясов
  • сравнение происходит по абсолютному времени

Типичные ошибки при работе с датами

Передача строки без coercion

z.date().parse("2024-01-01");

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

z.date().parse(new Date("bad"));

Ожидание автоматического парсинга

z.date().parse(1700000000000);

Комбинация с другими схемами

объект с датой

const schema = z.object({
  createdAt: z.coerce.date(),
  updatedAt: z.coerce.date().optional()
});

массив дат

const schema = z.array(z.coerce.date());

union с датой

const schema = z.union([
  z.date(),
  z.string().transform((v) => new Date(v))
]);

Работа с формами и внешними API

Частый сценарий — получение даты из JSON:

{
  "createdAt": "2024-01-01T12:00:00Z"
}

Схема:

const schema = z.object({
  createdAt: z.coerce.date()
});

Поведение:

  • вход строка
  • выход Date
  • автоматическая нормализация

Проверка строгих временных условий

запрет будущих дат

const schema = z.date().refine((date) => {
  return date <= new Date();
}, {
  message: "Дата не может быть в будущем"
});

запрет выходных

const schema = z.date().refine((date) => {
  const day = date.getDay();
  return day !== 0 && day !== 6;
});

Использование с сериализацией данных

При передаче данных через API часто требуется обратное преобразование:

const schema = z.date().transform((d) => d.toISOString());

Это обеспечивает:

  • совместимость с JSON
  • единый формат хранения
  • предсказуемость сериализации