How can zod date type accept ISO date strings?
Asked Answered
A

5

19

When defining a schema with zod, how do I use a date type?

If I use z.date() (see below) the date object is serialized to a ISO date string. But then if I try to parse it back with zod, the validator fails because a string is not a date.

import { z } from "zod"

const someTypeSchema = z.object({
    t: z.date(),
})
type SomeType = z.infer<typeof someTypeSchema>

function main() {
    const obj1: SomeType = {
        t: new Date(),
    }
    const stringified = JSON.stringify(obj1)
    console.log(stringified)
    const parsed = JSON.parse(stringified)
    const validated = someTypeSchema.parse(parsed) // <- Throws error! "Expected date, received string"
    console.log(validated.t)
}
main()
Anterior answered 18/1, 2023 at 9:47 Comment(3)
I found z.string().datetime() to work for me: github.com/colinhacks/zod#iso-datetimesWick
@KevinAshworth but your inferred type will then have a string property, right? Not a Date. But yes the validation will catch an a string that isn't a ISO date.Anterior
Yup. In my case, I'm dealing with date strings sent as strings from an API endpoint.Wick
A
8

Discovered that I can accept a string for the property, and use transform to turn it into a Date during parsing

import { z } from "zod"

const someTypeSchema = z.object({
    t: z.string().transform((str) => new Date(str)),
})
type SomeType = z.infer<typeof someTypeSchema>

function main() {
    const obj1: SomeType = {
        t: new Date(),
    }
    const stringified = JSON.stringify(obj1)
    console.log(stringified) // <-- {"t":"2023-09-07T07:19:51.128Z"}
    const parsed = JSON.parse(stringified)
    const validated = someTypeSchema.parse(parsed)
    console.log(validated.t instanceof Date) // <-- "true"
}
main()
Anterior answered 18/1, 2023 at 9:59 Comment(1)
you can do the same with coerce from string to date with z.string().pipe(z.coerce.date())Sevier
T
32

I used this from the README which works since v3.20 https://github.com/colinhacks/zod#dates

In your code snippet it would turn into this:

import { z } from "zod"

const someTypeSchema = z.object({
    t: z.coerce.date(),
})
type SomeType = z.infer<typeof someTypeSchema>
Trews answered 4/4, 2023 at 21:29 Comment(1)
that worked like a charm, tyLonganimity
K
11

The above answers didn't work for me,

however as suggested in the Zod doc this issue can be solved by using coerce for versions ^zod 3.20 as below

date: z.coerce.date();

and for version less than zod 3.20, the below worked for me as suggested in this issue

date: z.string().pipe(z.coerce.date())
Kmeson answered 22/9, 2023 at 11:33 Comment(0)
A
8

Discovered that I can accept a string for the property, and use transform to turn it into a Date during parsing

import { z } from "zod"

const someTypeSchema = z.object({
    t: z.string().transform((str) => new Date(str)),
})
type SomeType = z.infer<typeof someTypeSchema>

function main() {
    const obj1: SomeType = {
        t: new Date(),
    }
    const stringified = JSON.stringify(obj1)
    console.log(stringified) // <-- {"t":"2023-09-07T07:19:51.128Z"}
    const parsed = JSON.parse(stringified)
    const validated = someTypeSchema.parse(parsed)
    console.log(validated.t instanceof Date) // <-- "true"
}
main()
Anterior answered 18/1, 2023 at 9:59 Comment(1)
you can do the same with coerce from string to date with z.string().pipe(z.coerce.date())Sevier
S
1

I had a similar scenario where I needed to validate input birthdate in the format DD-MM-YYYY, which should be saved as a valid datetime ISO string (YYYY-MM-DDTHH:mm:ss.sssZ)

const BirthAtZod = z
  .string()
  .min(1, { message: 'mandatory' })
  // changes DD-MM-YYYY to YYYY-MM-DD
  .transform((v) => v.split('-').reverse().join('-'))
  // changes YYYY-MM-DD to YYYY-MM-DDT00:00:00.000Z
  .transform((v) => `${v}T00:00:00.000Z`)
  .pipe(
    z
      .string()
      .datetime({ message: 'incorrect format' }),
  );

const good = BirthAtZod.safeParse('20-10-2023');
if (good.success) {
  console.log(result.data) // 2023-10-20T00:00:00.000Z
}

const bad = BirthAtZod.safeParse('29-02-2023');
if (!bad.success) {
  console.error(bad.error.format()) // { _errors: ['incorrect format'] }
}
Sevier answered 10/10, 2023 at 10:49 Comment(0)
G
0

You have to expect a string instead

const someTypeSchema = z.object({
    t: z.string().refine((arg) =>
      arg.match(
        /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)((-(\d{2}):(\d{2})|Z)?)$/
    ))
})
Gschu answered 18/1, 2023 at 9:56 Comment(1)
but I want the property to be a Date. I found the answer, I can use transform to convert string to Date when parsing.Anterior

© 2022 - 2024 — McMap. All rights reserved.