Библиотека Superstruct предоставляет несколько способов валидации и преобразования дат. Основная сложность заключается в том, что в JavaScript дата может существовать в разных формах:
Date2026-05-12T10:00:00Z)DateSuperstruct позволяет:
Date;Для проверки экземпляра Date используется встроенный
struct date().
import { assert, date } from 'superstruct'
const UserSchema = date()
assert(new Date(), UserSchema)
Проверка проходит только для объектов класса Date.
assert('2026-05-12', date()) // Ошибка
В JavaScript объект Date может быть формально создан, но
содержать некорректное значение.
const invalid = new Date('wrong')
console.log(invalid instanceof Date) // true
console.log(isNaN(invalid.getTime())) // true
Superstruct считает такой объект валидным, потому что проверяется только тип.
import { assert, date } from 'superstruct'
const invalid = new Date('wrong')
assert(invalid, date()) // Ошибки нет
Для полноценной проверки требуется дополнительная валидация.
Функция refine() позволяет создавать дополнительное
правило проверки.
import { date, refine } from 'superstruct'
const ValidDate = refine(date(), 'ValidDate', value => {
return !isNaN(value.getTime())
})
Теперь схема отклоняет некорректные даты.
import { assert } from 'superstruct'
assert(new Date(), ValidDate)
assert(new Date('wrong'), ValidDate) // Ошибка
Во многих API даты приходят строками.
{
"createdAt": "2026-05-12T08:30:00Z"
}
Для проверки используется string() вместе с
refine().
import { string, refine } from 'superstruct'
const ISODateString = refine(
string(),
'ISODateString',
value => {
return !isNaN(Date.parse(value))
}
)
Использование:
ISODateString.create('2026-05-12T08:30:00Z')
Date.parse() принимает слишком много форматов. Для
строгой проверки применяется регулярное выражение.
import { string, refine } from 'superstruct'
const StrictISODate = refine(
string(),
'StrictISODate',
value => {
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)
}
)
Проверка:
StrictISODate.create('2026-05-12T10:20:30.000Z')
Superstruct поддерживает трансформацию данных через
coerce().
import { coerce, date, string } from 'superstruct'
const DateFromString = coerce(
date(),
string(),
value => new Date(value)
)
Использование:
const result = DateFromString.create(
'2026-05-12T10:00:00Z'
)
console.log(result instanceof Date) // true
Обычно преобразование совмещают с проверкой корректности.
import {
coerce,
date,
string,
refine
} from 'superstruct'
const ParsedDate = refine(
coerce(
date(),
string(),
value => new Date(value)
),
'ParsedDate',
value => !isNaN(value.getTime())
)
Теперь схема:
Date;Дата может отсутствовать.
{
deletedAt: null
}
Для этого используется nullable().
import {
nullable,
date
} from 'superstruct'
const NullableDate = nullable(date())
Допустимые значения:
new Date()
null
Если поле может отсутствовать полностью:
import {
optional,
date
} from 'superstruct'
const OptionalDate = optional(date())
Пример:
{
updatedAt: undefined
}
Чаще всего даты валидируются внутри объекта.
import {
object,
string,
date
} from 'superstruct'
const User = object({
id: string(),
createdAt: date(),
updatedAt: date()
})
import {
object,
string,
date,
coerce
} from 'superstruct'
const DateFromISO = coerce(
date(),
string(),
value => new Date(value)
)
const User = object({
id: string(),
createdAt: DateFromISO
})
Использование:
const user = User.create({
id: 'u1',
createdAt: '2026-05-12T08:00:00Z'
})
console.log(user.createdAt instanceof Date)
import {
refine,
date
} from 'superstruct'
const FutureDate = refine(
date(),
'FutureDate',
value => value.getTime() > Date.now()
)
const PastDate = refine(
date(),
'PastDate',
value => value.getTime() < Date.now()
)
const minDate = new Date('2025-01-01')
const DateAfter2025 = refine(
date(),
'DateAfter2025',
value => value >= minDate
)
const start = new Date('2025-01-01')
const end = new Date('2026-01-01')
const DateInRange = refine(
date(),
'DateInRange',
value => {
return value >= start && value <= end
}
)
Иногда дата приходит числом.
{
createdAt: 1747000000
}
Преобразование timestamp в Date:
import {
coerce,
date,
number
} from 'superstruct'
const DateFromTimestamp = coerce(
date(),
number(),
value => new Date(value * 1000)
)
const DateFromMs = coerce(
date(),
number(),
value => new Date(value)
)
Через union() можно принимать несколько типов.
import {
union,
string,
number,
date
} from 'superstruct'
const FlexibleDate = union([
string(),
number(),
date()
])
Однако такая схема только проверяет типы. Для полноценного парсинга
требуется coerce().
import {
union,
string,
number,
date,
coerce,
refine
} from 'superstruct'
const RawDate = union([
string(),
number(),
date()
])
const UniversalDate = refine(
coerce(
date(),
RawDate,
value => {
if (value instanceof Date) {
return value
}
if (typeof value === 'number') {
return new Date(value)
}
return new Date(value)
}
),
'UniversalDate',
value => !isNaN(value.getTime())
)
Поддерживаются:
'2026-05-12'
1747000000000
new Date()
Если дата отсутствует, можно подставлять текущее значение.
import {
defaulted,
date
} from 'superstruct'
const CreatedAt = defaulted(
date(),
() => new Date()
)
import {
object,
string,
defaulted,
date
} from 'superstruct'
const Post = object({
id: string(),
createdAt: defaulted(
date(),
() => new Date()
)
})
import {
array,
date
} from 'superstruct'
const DateArray = array(date())
import {
array,
string,
refine
} from 'superstruct'
const ISOString = refine(
string(),
'ISOString',
value => !isNaN(Date.parse(value))
)
const ISODateArray = array(ISOString)
import {
array,
string,
coerce,
date
} from 'superstruct'
const DateFromISO = coerce(
date(),
string(),
value => new Date(value)
)
const ParsedDateArray = array(DateFromISO)
import {
object,
array,
string
} from 'superstruct'
const Comment = object({
text: string(),
createdAt: DateFromISO
})
const Article = object({
title: string(),
comments: array(Comment)
})
import {
refine,
date
} from 'superstruct'
const ValidDate = refine(
date(),
'ValidDate',
value => !isNaN(value.getTime())
)
Перехват ошибки:
import { assert } from 'superstruct'
try {
assert(new Date('wrong'), ValidDate)
} catch (error) {
console.log(error.message)
}
Проверяет данные и выбрасывает исключение.
assert(value, schema)
Проверяет и преобразует данные.
const result = schema.create(value)
Для дат create() особенно полезен при использовании
coerce().
Метод validate() возвращает кортеж.
import { validate } from 'superstruct'
const [error, value] = validate(
'2026-05-12',
ParsedDate
)
Проверка:
if (error) {
console.error(error)
} else {
console.log(value)
}
import { Infer } from 'superstruct'
type ParsedDateType = Infer<typeof ParsedDate>
Результат:
Date
JavaScript автоматически учитывает timezone.
new Date('2026-05-12')
Такой формат интерпретируется как UTC.
Локальное время:
new Date('2026-05-12T10:00:00')
UTC-время:
new Date('2026-05-12T10:00:00Z')
При валидации API рекомендуется использовать ISO-формат с
Z.
import {
string,
refine
} from 'superstruct'
const DateOnly = refine(
string(),
'DateOnly',
value => {
return /^\d{4}-\d{2}-\d{2}$/.test(value)
}
)
const RFC3339 = refine(
string(),
'RFC3339',
value => {
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?(Z|[+-]\d{2}:\d{2})$/.test(value)
}
)
import {
object,
string,
coerce,
refine,
date
} from 'superstruct'
const APIDate = refine(
coerce(
date(),
string(),
value => new Date(value)
),
'APIDate',
value => !isNaN(value.getTime())
)
const Event = object({
id: string(),
title: string(),
startsAt: APIDate,
endsAt: APIDate
})
Superstruct позволяет проверять взаимосвязи между полями.
import {
object,
refine
} from 'superstruct'
const Event = refine(
object({
start: date(),
end: date()
}),
'EventDates',
value => {
return value.start < value.end
}
)
Иногда схема зависит от значения.
import {
dynamic,
string,
date
} from 'superstruct'
const FlexibleSchema = dynamic(value => {
if (typeof value === 'string') {
return string()
}
return date()
})
import {
nullable,
refine,
string
} from 'superstruct'
const NullableISODate = nullable(
refine(
string(),
'ISODate',
value => !isNaN(Date.parse(value))
)
)
import {
optional,
defaulted,
date
} from 'superstruct'
const UpdatedAt = defaulted(
optional(date()),
() => new Date()
)
import {
refine,
date
} from 'superstruct'
const AdultBirthDate = refine(
date(),
'AdultBirthDate',
value => {
const age =
new Date().getFullYear() -
value.getFullYear()
return age >= 18
}
)
const ExpirationDate = refine(
date(),
'ExpirationDate',
value => value > new Date()
)
date()
Проверяется лишь экземпляр Date, но не валидность
значения.
Date.parse('12/05/2026')
Результат зависит от среды выполнения и локали.
2026-05-12T10:00:00
Такой формат может интерпретироваться по-разному.
new Date(1747000000)
Получится дата 1970 года.
Для production-систем обычно используется комбинация:
refine(
coerce(
date(),
string(),
value => new Date(value)
),
'ValidDate',
value => !isNaN(value.getTime())
)
Такая схема:
Date;