Feature Request: ISO 8601 time interval
It would be nice to get some support for ISO 8601 time intervals.
Examples:
2025-03-11T06:03:33.572Z/2025-04-11T06:03:33.572Z(represents the time from 2025-03-11 till 2025-04-11)2025-04-11T06:03:33.572Z/P1Y(represents the time 2025-04-11 till 2026-04-11)P30D/2025-04-11T06:03:33.572Z(represents the time 2025-03-12 till 2026-04-11)
Requirements
The returns type should not be a plain string, but parse and extract some useful data:
const parsed = z.timeinterval().parse('2025-04-11T06:03:33.572Z/P1Y');
parsed.start; // 2025-04-11T06:03:33.572Z
parsed.end; // 2026-04-11T06:03:33.572Z
parsed.duration // {years: 1, months: 0, days: 0, hours: 0, minutes: 0, seconds: 0, milliseconds: 0}
...
And allow for some validation, like:
z.timeinterval({
minDate, // The interval can't include any date earlier than `minDate`
maxDate, // The interval can't include any date later than `maxDate`
minDuration, // The interval can't be shorter than `minDuration`
maxDuration, // The interval can't be longer than `maxDuration`
requireDifferent, // Valid values `days`|`months`|`milliseconds`\...; The interval start/end can't be the same date, month, millisecond. This is useful to prevent 0 duration intervals.
})
Technical implementation
This is where things get difficult ...
There are no native objects to represent a time interval.
There are early proposals to represent durations:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration
So parsing of the string interval and the return type would have to be manually developed.
Looking at the Luxon implementation of the time interval, it's complex enough to question whether this should be implemented into Zod: https://github.com/moment/luxon/blob/master/src/interval.js (Especially given that other concepts like duration would also have to be developed).
So it would likely be best to integrate a library like Luxon (zero dependencies) into Zod for this type of validation. Though Zod itself aims to have zero dependencies ...
So I see a couple technical paths forward:
- Accept that for such more complex validation it's better to delegate responsibility and use Luxon
- Add Luxon as optional dependency, document properly that users have to manually install Luxon to use timeinterval
- Build and provide some extension to zod that patches the time interval validation (and use Luxon there)
- Is there a concept for extensions in zod?
- It kinda works via
.transform/.refine, however, a more native integration would be nice.
- Copy and build the time interval validation into zod itself
Example time interval validation based on luxon:
import {z} from 'zod';
import {DateTime, Duration, Interval} from 'luxon';
import {Invalid, Valid} from 'luxon/src/_util';
import {DurationUnit} from 'luxon/src/duration';
/**
* Creates a zod validator for a time interval parameter used in our APIs.
* To be used to validate the request query parameter such as `interval`.
*
* E.g. for a interval string like this:
* `2024-03-01T13:00:00Z/2024-03-31T13:00:00Z` or
* `2024-03-01T13:00:00Z/P30D` or
* `P30D/2024-03-01T13:00:00Z`
* an example validator accepting the input would look like this:
*
* ```
* interval: createTimeIntervalValidator({
* minDuration: '1w',
* maxDuration: '4w',
* minDate: new Date('2024-01-01T12:00:00Z'),
* maxDate: new Date('2025-01-01T12:00:00Z'),
* });
* ```
*
* @param options Configuration options defining which intervals can be provided.
* @param options.minDuration The minimum duration a time interval can to be.
* @param options.maxDuration The maximum duration a time interval can to be.
* @param options.minDate The earliest date that a time interval can be.
* @param options.maxDate The latest date that a time interval can be.
* @param options.requireDifferent Requires that the start and end dates
have a different month/day/hour/millisecond/...
*
*/
export function createTimeIntervalValidator({
minDuration = null,
maxDuration = null,
minDate = null,
maxDate = null,
requireDifferent = 'milliseconds',
}: {
maxDuration?: Duration | null;
minDuration?: Duration | null;
minDate?: DateTime | null;
maxDate?: DateTime | null;
requireDifferent?: DurationUnit | null;
} = {}) {
return z.string().transform((inputString, ctx) => {
const interval = Interval.fromISO(inputString);
if (!isValidInterval(interval)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Not a valid ISO 8601 time interval.',
});
return z.NEVER;
}
if (minDate && !interval.isAfter(minDate)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Interval must start after ${minDate.toISO()}`,
});
return z.NEVER;
}
if (maxDate && !interval.isBefore(maxDate)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Interval must end before ${maxDate.toISO()}`,
});
return z.NEVER;
}
if (
minDuration &&
interval.length('milliseconds') >= minDuration.as('milliseconds')
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Interval be equal or longer than ${minDuration.toISO()}`,
});
return z.NEVER;
}
if (
maxDuration &&
interval.length('milliseconds') <= maxDuration.as('milliseconds')
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Interval be equal or shorter than ${maxDuration.toISO()}`,
});
return z.NEVER;
}
if (requireDifferent && interval.hasSame(requireDifferent)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message:
`Interval must have different ${requireDifferent} for start and end`,
});
return z.NEVER;
}
return interval;
});
}
function isValidInterval(
interval: Interval<Valid> | Interval<Invalid>,
): interval is Interval<Valid> {
return interval.isValid;
}
I disagree. It looks out of the scope of Zod. You're looking for a specific date library. I don't see what's the benefit of removing a specific feature from Luxon and translatin the code to Zod, increasing the bundle size
@Christopher96u I kinda agree, I also don't see having this natively built into Zod now that I learned the complexity of the parsing 🤔
Though there is definitely some need to have such validation be possible with Zod (in case someone needs it). I'm more on the side of providing this functionality as a dedicated package.
I'm unsure, however, what the best way would be to extend Zod with such functionality...
I'm unsure, however, what the best way would be to extend Zod with such functionality...
You shouldn't need to extend Zod to support his use case, you can use any predicate function in refine, and that's what I would suggest anyone who wants so custom validation (not parsing, but validation) logic to use. This won't change your string into an Interval for you, I think it should be up to the end user how they want to represent this.
I do think that people can and should publish ecosystem packages that come with nice check and transform functions that can be used with zod and @zod/mini!
Hi, @IchordeDionysos. I'm Dosu, and I'm helping the Zod team manage their backlog. I'm marking this issue as stale.
Issue Summary:
- The issue requests support for parsing ISO 8601 time intervals into a structured format.
- @Christopher96u suggests this feature is outside Zod's scope, recommending a date library instead.
- You agree and consider creating a dedicated package for this functionality.
- @scotttrinh recommends using Zod's
refinemethod for custom validation and encourages ecosystem package development.
Next Steps:
- Please confirm if this issue is still relevant to the latest version of Zod. If so, feel free to keep the discussion open by commenting here.
- Otherwise, this issue will be automatically closed in 28 days.
Thank you for your understanding and contribution!
I think this issue is worth keeping open.
The proposal is at Stage 3, and while the timeline for adoption is uncertain, implementations are advancing. Support is available in Firefox, and active progress is being tracked for Chrome.
I think native support for Temporal types seems like a natural fit for Zod.
Hi, @IchordeDionysos. I'm Dosu, and I'm helping the Zod team manage their backlog and am marking this issue as stale.
Issue Summary:
- You requested ISO 8601 time interval parsing in Zod with structured output and validation options, sharing a Luxon-based example.
- Some contributors noted that this functionality might be outside Zod’s core scope and better suited for dedicated date/time libraries or ecosystem packages.
- There was discussion about the evolving native Temporal API and how native Temporal integration could be a good fit for Zod in the future.
- The issue remains open without a resolution or official support for this feature in Zod itself.
Next Steps:
- Please let me know if this feature is still relevant to your use case with the latest version of Zod or if you have a minimal reproduction or package proposal.
- Otherwise, I will automatically close this issue in 7 days but it can be reopened or referenced later if needed.
Thanks for your understanding and contribution!
I'm unable to tag dosubot directly @dosu @dosubot , this issue is not stale and is a valid feature request.