Skip to main content

Overview

Zod provides validators for ISO 8601 datetime formats under the z.iso namespace:
  • z.iso.datetime() - ISO 8601 datetime strings
  • z.iso.date() - ISO 8601 date strings (YYYY-MM-DD)
  • z.iso.time() - ISO 8601 time strings (HH:MM:SS)
  • z.iso.duration() - ISO 8601 duration strings
import { z } from "zod";

const datetimeSchema = z.iso.datetime();
const dateSchema = z.iso.date();
const timeSchema = z.iso.time();
const durationSchema = z.iso.duration();
Location in source: ~/workspace/source/packages/zod/src/v4/classic/iso.ts

z.iso.datetime()

Validates ISO 8601 datetime strings with various precision and timezone options.

Basic Usage

const schema = z.iso.datetime();

schema.parse("2022-10-13T09:52:31.816Z"); // ✓ passes
schema.parse("2020-10-14"); // ✗ throws - missing time

Valid Examples (Default)

By default, requires UTC timezone indicator (Z):
const schema = z.iso.datetime();

schema.parse("1970-01-01T00:00:00.000Z");
schema.parse("2022-10-13T09:52:31.816Z");
schema.parse("2022-10-13T09:52:31.8162314Z"); // variable precision
schema.parse("1970-01-01T00:00:00Z"); // no milliseconds
schema.parse("2022-10-13T09:52:31Z");

Invalid Examples (Default)

const schema = z.iso.datetime();

schema.safeParse("").success; // false - empty
schema.safeParse("foo").success; // false - not a datetime
schema.safeParse("2020-10-14").success; // false - date only
schema.safeParse("T18:45:12.123").success; // false - time only
schema.safeParse("2020-10-14T17:42:29+00:00").success; // false - offset not allowed (default)

Precision Options

Control millisecond precision with the precision option:

No Precision Constraint (Default)

Accepts any number of fractional seconds:
const schema = z.iso.datetime(); // precision not specified

schema.parse("2022-10-13T09:52:31Z"); // ✓ no fractional seconds
schema.parse("2022-10-13T09:52:31.1Z"); // ✓ 1 digit
schema.parse("2022-10-13T09:52:31.816Z"); // ✓ 3 digits
schema.parse("2022-10-13T09:52:31.8162314Z"); // ✓ 7 digits

Precision: -1 (No Fractional Seconds)

Requires no fractional seconds:
const schema = z.iso.datetime({ precision: -1 });

schema.parse("1970-01-01T00:00Z"); // ✓ passes
schema.parse("2022-10-13T09:52Z"); // ✓ passes

schema.safeParse("1970-01-01T00:00:00.000Z").success; // false
schema.safeParse("2022-10-13T09:52:31.816Z").success; // false

Precision: 0 (Seconds Only)

Requires seconds but no fractional part:
const schema = z.iso.datetime({ precision: 0 });

schema.parse("1970-01-01T00:00:00Z"); // ✓ passes
schema.parse("2022-10-13T09:52:31Z"); // ✓ passes

schema.safeParse("1970-01-01T00:00:00.000Z").success; // false - has fractional
schema.safeParse("2022-10-13T09:52Z").success; // false - missing seconds

Precision: 3 (Milliseconds)

Requires exactly 3 digits of fractional seconds:
const schema = z.iso.datetime({ precision: 3 });

schema.parse("1970-01-01T00:00:00.000Z"); // ✓ passes
schema.parse("2022-10-13T09:52:31.123Z"); // ✓ passes

schema.safeParse("1970-01-01T00:00:00.1Z").success; // false - only 1 digit
schema.safeParse("1970-01-01T00:00:00.12Z").success; // false - only 2 digits
schema.safeParse("2022-10-13T09:52:31Z").success; // false - no fractional

Precision: 4+ (Custom)

Requires exact number of fractional digits:
const schema = z.iso.datetime({ precision: 4 });

schema.parse("1970-01-01T00:00:00.1234Z"); // ✓ passes
schema.safeParse("1970-01-01T00:00:00.123Z").success; // false - only 3 digits

Offset Options

Control timezone offset handling with the offset option:

offset: false (Default)

Only accepts UTC timezone (Z):
const schema = z.iso.datetime(); // offset: false by default

schema.parse("2022-10-13T09:52:31.816Z"); // ✓ passes
schema.safeParse("2020-10-14T17:42:29+00:00").success; // false - offset not allowed

offset: true

Accepts both UTC (Z) and timezone offsets:
const schema = z.iso.datetime({ offset: true });

schema.parse("1970-01-01T00:00:00.000Z"); // ✓ UTC
schema.parse("2020-10-14T17:42:29+00:00"); // ✓ offset
schema.parse("2020-10-14T17:42:29+03:15"); // ✓ custom offset
schema.parse("2020-10-14T17:42:29-05:00"); // ✓ negative offset

// Invalid offsets
schema.safeParse("2020-10-14T17:42:29+0315").success; // false - missing colon
schema.safeParse("2020-10-14T17:42:29+03").success; // false - incomplete
schema.safeParse("2020-10-14T17:42:29+24:00").success; // false - out of range

Local Time Option

Allow local time (no timezone indicator) with the local option:

local: false (Default)

Requires timezone indicator:
const schema = z.iso.datetime(); // local: false by default

schema.parse("2022-10-13T09:52:31Z"); // ✓ passes
schema.safeParse("2022-10-13T09:52:31").success; // false - no timezone

local: true

Accepts both local time and times with timezone:
const schema = z.iso.datetime({ local: true });

schema.parse("1970-01-01T00:00"); // ✓ local time (HH:MM)
schema.parse("1970-01-01T00:00:00"); // ✓ local time (HH:MM:SS)
schema.parse("2022-10-13T09:52:31.816"); // ✓ local with milliseconds
schema.parse("1970-01-01T00:00:00.000"); // ✓ local with milliseconds

// Still invalid
schema.safeParse("2022-10-13 09:52:31").success; // false - space instead of T
schema.safeParse("2022-10-13T24:52:31").success; // false - invalid hour

Combining local and offset

const schema = z.iso.datetime({ local: true, offset: true });

schema.parse("2022-10-13T12:52:00"); // ✓ local time
schema.parse("2022-10-13T12:52:00Z"); // ✓ UTC
schema.parse("2022-10-13T12:52+02:00"); // ✓ with offset

z.iso.date()

Validates ISO 8601 date strings in YYYY-MM-DD format.

Basic Usage

const schema = z.iso.date();

schema.parse("2022-10-31"); // ✓ passes
schema.parse("2022-10-14T17:42:29Z"); // ✗ throws - includes time

Valid Examples

const schema = z.iso.date();

schema.parse("1970-01-01");
schema.parse("2022-01-31");
schema.parse("2022-12-31");
schema.parse("2000-02-29"); // leap year
schema.parse("2400-02-29"); // leap year

Invalid Examples

const schema = z.iso.date();

// Wrong format
schema.safeParse("").success; // false
schema.safeParse("foo").success; // false
schema.safeParse("200-01-01").success; // false - year too short
schema.safeParse("20000-01-01").success; // false - year too long
schema.safeParse("2000-0-01").success; // false - month too short
schema.safeParse("2000-01-0").success; // false - day too short
schema.safeParse("2000/01/01").success; // false - wrong separator
schema.safeParse("01-01-2022").success; // false - wrong order

// Invalid dates
schema.safeParse("2000-00-12").success; // false - month 0
schema.safeParse("2000-12-00").success; // false - day 0
schema.safeParse("2000-01-32").success; // false - day 32
schema.safeParse("2000-13-01").success; // false - month 13
schema.safeParse("2022-02-29").success; // false - not a leap year
schema.safeParse("2000-04-31").success; // false - April has 30 days

// Contains time
schema.safeParse("2020-10-14T17:42:29Z").success; // false

z.iso.time()

Validates ISO 8601 time strings (HH:MM:SS).

Basic Usage

const schema = z.iso.time();

schema.parse("09:52:31"); // ✓ passes
schema.parse("T18:45:12.123"); // ✗ throws - has T prefix

Valid Examples

const schema = z.iso.time();

schema.parse("00:00:00");
schema.parse("23:00:00");
schema.parse("00:59:00");
schema.parse("00:00:59");
schema.parse("23:59:59");
schema.parse("09:52:31");
schema.parse("23:59:59.9999999"); // with fractional seconds

Precision Option

Similar to datetime, you can specify precision:
const schema = z.iso.time({ precision: 3 });

schema.parse("09:52:31.123"); // ✓ exactly 3 digits
schema.safeParse("09:52:31").success; // false - no fractional
schema.safeParse("09:52:31.12").success; // false - only 2 digits

z.iso.duration()

Validates ISO 8601 duration strings.

Basic Usage

const schema = z.iso.duration();

schema.parse("P1Y2M3DT4H5M6S"); // ✓ passes

Valid Examples

const schema = z.iso.duration();

schema.parse("P1Y"); // 1 year
schema.parse("P3Y6M4DT12H30M5S"); // 3 years, 6 months, 4 days, 12 hours, 30 minutes, 5 seconds
schema.parse("P23DT23H"); // 23 days, 23 hours
schema.parse("PT36H"); // 36 hours
schema.parse("P1W"); // 1 week

Parameters

All ISO datetime functions accept optional parameters:
z.iso.datetime(params?: DateTimeParams)
z.iso.date(params?: string | DateParams)
z.iso.time(params?: TimeParams)
z.iso.duration(params?: string | DurationParams)

DateTimeParams

{
  precision?: number | null; // -1, 0, 3, etc.
  offset?: boolean;          // allow timezone offsets
  local?: boolean;           // allow local time
  message?: string;          // custom error message
}

TimeParams

{
  precision?: number | null; // fractional seconds precision
  message?: string;          // custom error message
}

Examples

API Timestamp Validation

const eventSchema = z.object({
  id: z.uuid(),
  timestamp: z.iso.datetime(),
  type: z.string(),
});

eventSchema.parse({
  id: "550e8400-e29b-41d4-a716-446655440000",
  timestamp: "2022-10-13T09:52:31.816Z",
  type: "user.created"
}); // ✓ passes

Flexible Timezone Handling

const schema = z.object({
  createdAt: z.iso.datetime({ offset: true }),
  updatedAt: z.iso.datetime({ local: true, offset: true }),
});

schema.parse({
  createdAt: "2022-10-13T09:52:31.816Z",
  updatedAt: "2022-10-14T10:30:00" // local time allowed
}); // ✓ passes

Date Range Validation

const dateRangeSchema = z.object({
  startDate: z.iso.date(),
  endDate: z.iso.date(),
}).refine(
  (data) => data.startDate <= data.endDate,
  "End date must be after start date"
);

dateRangeSchema.parse({
  startDate: "2022-01-01",
  endDate: "2022-12-31"
}); // ✓ passes

Schedule with Time

const scheduleSchema = z.object({
  date: z.iso.date(),
  startTime: z.iso.time(),
  endTime: z.iso.time(),
  duration: z.iso.duration().optional(),
});

scheduleSchema.parse({
  date: "2022-10-14",
  startTime: "09:00:00",
  endTime: "17:00:00",
  duration: "PT8H"
}); // ✓ passes

High-Precision Timestamps

const highPrecisionSchema = z.object({
  timestamp: z.iso.datetime({ precision: 6 }),
  measuredValue: z.number(),
});

highPrecisionSchema.parse({
  timestamp: "2022-10-13T09:52:31.816234Z",
  measuredValue: 42.5
}); // ✓ passes

Return Type

All ISO datetime schemas return strings:
type Output = string;
type Input = string;
The ISO datetime validators perform format validation only. They do not parse the string into a Date object or validate that the date is semantically valid (e.g., they accept “2022-02-30” on some validators). For full date validation including semantic checks, combine with additional refinements or use z.date() with coercion.
When using local: true, be aware that the datetime string does not contain timezone information. This can lead to ambiguity when the string is parsed in different timezones. For unambiguous timestamps, prefer using UTC (Z) or explicit offsets.

Build docs developers (and LLMs) love