Skip to content

Commit c73c12e

Browse files
authored
make ZodString.date(), ZodString.time(), and ZodString.datetime() only accept valid date and time (#3391)
* update tests * add tests for passing 00 as day or month * move code to src * make datetime only accept valid times * add test cases
1 parent 9cc890d commit c73c12e

File tree

4 files changed

+78
-6
lines changed

4 files changed

+78
-6
lines changed

deno/lib/__tests__/string.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,19 @@ test("date", () => {
517517
test("date parsing", () => {
518518
const date = z.string().date();
519519
date.parse("1970-01-01");
520-
date.parse("2022-10-13");
520+
date.parse("2022-01-31");
521+
date.parse("2022-02-29");
522+
date.parse("2022-03-31");
523+
date.parse("2022-04-30");
524+
date.parse("2022-05-31");
525+
date.parse("2022-06-30");
526+
date.parse("2022-07-31");
527+
date.parse("2022-08-31");
528+
date.parse("2022-09-30");
529+
date.parse("2022-10-31");
530+
date.parse("2022-11-30");
531+
date.parse("2022-12-31");
532+
521533
expect(() => date.parse("")).toThrow();
522534
expect(() => date.parse("foo")).toThrow();
523535
expect(() => date.parse("200-01-01")).toThrow();
@@ -534,6 +546,19 @@ test("date parsing", () => {
534546
expect(() => date.parse("2020-10-14T17:42:29Z")).toThrow();
535547
expect(() => date.parse("2020-10-14T17:42:29")).toThrow();
536548
expect(() => date.parse("2020-10-14T17:42:29.123Z")).toThrow();
549+
550+
expect(() => date.parse("2000-00-12")).toThrow();
551+
expect(() => date.parse("2000-12-00")).toThrow();
552+
expect(() => date.parse("2000-01-32")).toThrow();
553+
expect(() => date.parse("2000-13-01")).toThrow();
554+
expect(() => date.parse("2000-21-01")).toThrow();
555+
556+
expect(() => date.parse("2000-02-30")).toThrow();
557+
expect(() => date.parse("2000-02-31")).toThrow();
558+
expect(() => date.parse("2000-04-31")).toThrow();
559+
expect(() => date.parse("2000-06-31")).toThrow();
560+
expect(() => date.parse("2000-09-31")).toThrow();
561+
expect(() => date.parse("2000-11-31")).toThrow();
537562
});
538563

539564
test("time", () => {
@@ -544,6 +569,10 @@ test("time", () => {
544569
test("time parsing", () => {
545570
const time = z.string().time();
546571
time.parse("00:00:00");
572+
time.parse("23:00:00");
573+
time.parse("00:59:00");
574+
time.parse("00:00:59");
575+
time.parse("23:59:59");
547576
time.parse("09:52:31");
548577
time.parse("23:59:59.9999999");
549578
expect(() => time.parse("")).toThrow();
@@ -554,6 +583,11 @@ test("time parsing", () => {
554583
expect(() => time.parse("00:00:0")).toThrow();
555584
expect(() => time.parse("00:00:00.000+00:00")).toThrow();
556585

586+
expect(() => time.parse("24:00:00")).toThrow();
587+
expect(() => time.parse("00:60:00")).toThrow();
588+
expect(() => time.parse("00:00:60")).toThrow();
589+
expect(() => time.parse("24:60:60")).toThrow();
590+
557591
const time2 = z.string().time({ precision: 2 });
558592
time2.parse("00:00:00.00");
559593
time2.parse("09:52:31.12");

deno/lib/types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,11 +600,13 @@ const ipv4Regex =
600600
const ipv6Regex =
601601
/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;
602602

603-
const dateRegexSource = `\\d{4}-\\d{2}-\\d{2}`;
603+
// const dateRegexSource = `\\d{4}-\\d{2}-\\d{2}`;
604+
const dateRegexSource = `\\d{4}-((0[13578]|10|12)-31|(0[13-9]|1[0-2])-30|(0[1-9]|1[0-2])-(0[1-9]|1\\d|2\\d))`;
604605
const dateRegex = new RegExp(`^${dateRegexSource}$`);
605606

606607
function timeRegexSource(args: { precision?: number | null }) {
607-
let regex = `\\d{2}:\\d{2}:\\d{2}`;
608+
// let regex = `\\d{2}:\\d{2}:\\d{2}`;
609+
let regex = `([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d`;
608610
if (args.precision) {
609611
regex = `${regex}\\.\\d{${args.precision}}`;
610612
} else if (args.precision == null) {

src/__tests__/string.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,19 @@ test("date", () => {
516516
test("date parsing", () => {
517517
const date = z.string().date();
518518
date.parse("1970-01-01");
519-
date.parse("2022-10-13");
519+
date.parse("2022-01-31");
520+
date.parse("2022-02-29");
521+
date.parse("2022-03-31");
522+
date.parse("2022-04-30");
523+
date.parse("2022-05-31");
524+
date.parse("2022-06-30");
525+
date.parse("2022-07-31");
526+
date.parse("2022-08-31");
527+
date.parse("2022-09-30");
528+
date.parse("2022-10-31");
529+
date.parse("2022-11-30");
530+
date.parse("2022-12-31");
531+
520532
expect(() => date.parse("")).toThrow();
521533
expect(() => date.parse("foo")).toThrow();
522534
expect(() => date.parse("200-01-01")).toThrow();
@@ -533,6 +545,19 @@ test("date parsing", () => {
533545
expect(() => date.parse("2020-10-14T17:42:29Z")).toThrow();
534546
expect(() => date.parse("2020-10-14T17:42:29")).toThrow();
535547
expect(() => date.parse("2020-10-14T17:42:29.123Z")).toThrow();
548+
549+
expect(() => date.parse("2000-00-12")).toThrow();
550+
expect(() => date.parse("2000-12-00")).toThrow();
551+
expect(() => date.parse("2000-01-32")).toThrow();
552+
expect(() => date.parse("2000-13-01")).toThrow();
553+
expect(() => date.parse("2000-21-01")).toThrow();
554+
555+
expect(() => date.parse("2000-02-30")).toThrow();
556+
expect(() => date.parse("2000-02-31")).toThrow();
557+
expect(() => date.parse("2000-04-31")).toThrow();
558+
expect(() => date.parse("2000-06-31")).toThrow();
559+
expect(() => date.parse("2000-09-31")).toThrow();
560+
expect(() => date.parse("2000-11-31")).toThrow();
536561
});
537562

538563
test("time", () => {
@@ -543,6 +568,10 @@ test("time", () => {
543568
test("time parsing", () => {
544569
const time = z.string().time();
545570
time.parse("00:00:00");
571+
time.parse("23:00:00");
572+
time.parse("00:59:00");
573+
time.parse("00:00:59");
574+
time.parse("23:59:59");
546575
time.parse("09:52:31");
547576
time.parse("23:59:59.9999999");
548577
expect(() => time.parse("")).toThrow();
@@ -553,6 +582,11 @@ test("time parsing", () => {
553582
expect(() => time.parse("00:00:0")).toThrow();
554583
expect(() => time.parse("00:00:00.000+00:00")).toThrow();
555584

585+
expect(() => time.parse("24:00:00")).toThrow();
586+
expect(() => time.parse("00:60:00")).toThrow();
587+
expect(() => time.parse("00:00:60")).toThrow();
588+
expect(() => time.parse("24:60:60")).toThrow();
589+
556590
const time2 = z.string().time({ precision: 2 });
557591
time2.parse("00:00:00.00");
558592
time2.parse("09:52:31.12");

src/types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,11 +600,13 @@ const ipv4Regex =
600600
const ipv6Regex =
601601
/^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/;
602602

603-
const dateRegexSource = `\\d{4}-\\d{2}-\\d{2}`;
603+
// const dateRegexSource = `\\d{4}-\\d{2}-\\d{2}`;
604+
const dateRegexSource = `\\d{4}-((0[13578]|10|12)-31|(0[13-9]|1[0-2])-30|(0[1-9]|1[0-2])-(0[1-9]|1\\d|2\\d))`;
604605
const dateRegex = new RegExp(`^${dateRegexSource}$`);
605606

606607
function timeRegexSource(args: { precision?: number | null }) {
607-
let regex = `\\d{2}:\\d{2}:\\d{2}`;
608+
// let regex = `\\d{2}:\\d{2}:\\d{2}`;
609+
let regex = `([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d`;
608610
if (args.precision) {
609611
regex = `${regex}\\.\\d{${args.precision}}`;
610612
} else if (args.precision == null) {

0 commit comments

Comments
 (0)