Skip to content

Commit

Permalink
make ZodString.date(), ZodString.time(), and `ZodString.datetime(…
Browse files Browse the repository at this point in the history
…)` 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
  • Loading branch information
szamanr authored Apr 12, 2024
1 parent 9cc890d commit c73c12e
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 6 deletions.
36 changes: 35 additions & 1 deletion deno/lib/__tests__/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,19 @@ test("date", () => {
test("date parsing", () => {
const date = z.string().date();
date.parse("1970-01-01");
date.parse("2022-10-13");
date.parse("2022-01-31");
date.parse("2022-02-29");
date.parse("2022-03-31");
date.parse("2022-04-30");
date.parse("2022-05-31");
date.parse("2022-06-30");
date.parse("2022-07-31");
date.parse("2022-08-31");
date.parse("2022-09-30");
date.parse("2022-10-31");
date.parse("2022-11-30");
date.parse("2022-12-31");

expect(() => date.parse("")).toThrow();
expect(() => date.parse("foo")).toThrow();
expect(() => date.parse("200-01-01")).toThrow();
Expand All @@ -534,6 +546,19 @@ test("date parsing", () => {
expect(() => date.parse("2020-10-14T17:42:29Z")).toThrow();
expect(() => date.parse("2020-10-14T17:42:29")).toThrow();
expect(() => date.parse("2020-10-14T17:42:29.123Z")).toThrow();

expect(() => date.parse("2000-00-12")).toThrow();
expect(() => date.parse("2000-12-00")).toThrow();
expect(() => date.parse("2000-01-32")).toThrow();
expect(() => date.parse("2000-13-01")).toThrow();
expect(() => date.parse("2000-21-01")).toThrow();

expect(() => date.parse("2000-02-30")).toThrow();
expect(() => date.parse("2000-02-31")).toThrow();
expect(() => date.parse("2000-04-31")).toThrow();
expect(() => date.parse("2000-06-31")).toThrow();
expect(() => date.parse("2000-09-31")).toThrow();
expect(() => date.parse("2000-11-31")).toThrow();
});

test("time", () => {
Expand All @@ -544,6 +569,10 @@ test("time", () => {
test("time parsing", () => {
const time = z.string().time();
time.parse("00:00:00");
time.parse("23:00:00");
time.parse("00:59:00");
time.parse("00:00:59");
time.parse("23:59:59");
time.parse("09:52:31");
time.parse("23:59:59.9999999");
expect(() => time.parse("")).toThrow();
Expand All @@ -554,6 +583,11 @@ test("time parsing", () => {
expect(() => time.parse("00:00:0")).toThrow();
expect(() => time.parse("00:00:00.000+00:00")).toThrow();

expect(() => time.parse("24:00:00")).toThrow();
expect(() => time.parse("00:60:00")).toThrow();
expect(() => time.parse("00:00:60")).toThrow();
expect(() => time.parse("24:60:60")).toThrow();

const time2 = z.string().time({ precision: 2 });
time2.parse("00:00:00.00");
time2.parse("09:52:31.12");
Expand Down
6 changes: 4 additions & 2 deletions deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,11 +600,13 @@ const ipv4Regex =
const ipv6Regex =
/^(([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})))$/;

const dateRegexSource = `\\d{4}-\\d{2}-\\d{2}`;
// const dateRegexSource = `\\d{4}-\\d{2}-\\d{2}`;
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))`;
const dateRegex = new RegExp(`^${dateRegexSource}$`);

function timeRegexSource(args: { precision?: number | null }) {
let regex = `\\d{2}:\\d{2}:\\d{2}`;
// let regex = `\\d{2}:\\d{2}:\\d{2}`;
let regex = `([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d`;
if (args.precision) {
regex = `${regex}\\.\\d{${args.precision}}`;
} else if (args.precision == null) {
Expand Down
36 changes: 35 additions & 1 deletion src/__tests__/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,19 @@ test("date", () => {
test("date parsing", () => {
const date = z.string().date();
date.parse("1970-01-01");
date.parse("2022-10-13");
date.parse("2022-01-31");
date.parse("2022-02-29");
date.parse("2022-03-31");
date.parse("2022-04-30");
date.parse("2022-05-31");
date.parse("2022-06-30");
date.parse("2022-07-31");
date.parse("2022-08-31");
date.parse("2022-09-30");
date.parse("2022-10-31");
date.parse("2022-11-30");
date.parse("2022-12-31");

expect(() => date.parse("")).toThrow();
expect(() => date.parse("foo")).toThrow();
expect(() => date.parse("200-01-01")).toThrow();
Expand All @@ -533,6 +545,19 @@ test("date parsing", () => {
expect(() => date.parse("2020-10-14T17:42:29Z")).toThrow();
expect(() => date.parse("2020-10-14T17:42:29")).toThrow();
expect(() => date.parse("2020-10-14T17:42:29.123Z")).toThrow();

expect(() => date.parse("2000-00-12")).toThrow();
expect(() => date.parse("2000-12-00")).toThrow();
expect(() => date.parse("2000-01-32")).toThrow();
expect(() => date.parse("2000-13-01")).toThrow();
expect(() => date.parse("2000-21-01")).toThrow();

expect(() => date.parse("2000-02-30")).toThrow();
expect(() => date.parse("2000-02-31")).toThrow();
expect(() => date.parse("2000-04-31")).toThrow();
expect(() => date.parse("2000-06-31")).toThrow();
expect(() => date.parse("2000-09-31")).toThrow();
expect(() => date.parse("2000-11-31")).toThrow();
});

test("time", () => {
Expand All @@ -543,6 +568,10 @@ test("time", () => {
test("time parsing", () => {
const time = z.string().time();
time.parse("00:00:00");
time.parse("23:00:00");
time.parse("00:59:00");
time.parse("00:00:59");
time.parse("23:59:59");
time.parse("09:52:31");
time.parse("23:59:59.9999999");
expect(() => time.parse("")).toThrow();
Expand All @@ -553,6 +582,11 @@ test("time parsing", () => {
expect(() => time.parse("00:00:0")).toThrow();
expect(() => time.parse("00:00:00.000+00:00")).toThrow();

expect(() => time.parse("24:00:00")).toThrow();
expect(() => time.parse("00:60:00")).toThrow();
expect(() => time.parse("00:00:60")).toThrow();
expect(() => time.parse("24:60:60")).toThrow();

const time2 = z.string().time({ precision: 2 });
time2.parse("00:00:00.00");
time2.parse("09:52:31.12");
Expand Down
6 changes: 4 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,11 +600,13 @@ const ipv4Regex =
const ipv6Regex =
/^(([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})))$/;

const dateRegexSource = `\\d{4}-\\d{2}-\\d{2}`;
// const dateRegexSource = `\\d{4}-\\d{2}-\\d{2}`;
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))`;
const dateRegex = new RegExp(`^${dateRegexSource}$`);

function timeRegexSource(args: { precision?: number | null }) {
let regex = `\\d{2}:\\d{2}:\\d{2}`;
// let regex = `\\d{2}:\\d{2}:\\d{2}`;
let regex = `([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d`;
if (args.precision) {
regex = `${regex}\\.\\d{${args.precision}}`;
} else if (args.precision == null) {
Expand Down

0 comments on commit c73c12e

Please sign in to comment.