Skip to content

Commit

Permalink
Disallow hour-only offsets
Browse files Browse the repository at this point in the history
  • Loading branch information
colinhacks committed Apr 17, 2024
1 parent c54e109 commit 35f0a38
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 14 deletions.
8 changes: 5 additions & 3 deletions deno/lib/__tests__/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ test("datetime parsing", () => {
datetimeOffset.parse("2020-10-14T17:42:29+00:00");
datetimeOffset.parse("2020-10-14T17:42:29+03:15");
datetimeOffset.parse("2020-10-14T17:42:29+0315");
datetimeOffset.parse("2020-10-14T17:42:29+03");
expect(() => datetimeOffset.parse("2020-10-14T17:42:29+03"));
expect(() => datetimeOffset.parse("tuna")).toThrow();
expect(() => datetimeOffset.parse("2022-10-13T09:52:31.Z")).toThrow();

Expand All @@ -525,7 +525,7 @@ test("datetime parsing", () => {
datetimeOffsetNoMs.parse("2022-10-13T09:52:31Z");
datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00:00");
datetimeOffsetNoMs.parse("2020-10-14T17:42:29+0000");
datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00");
expect(() => datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00")).toThrow();
expect(() => datetimeOffsetNoMs.parse("tuna")).toThrow();
expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00:00.000Z")).toThrow();
expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00:00.Z")).toThrow();
Expand All @@ -538,7 +538,9 @@ test("datetime parsing", () => {
datetimeOffset4Ms.parse("1970-01-01T00:00:00.1234Z");
datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00:00");
datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+0000");
datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00");
expect(() =>
datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00")
).toThrow();
expect(() => datetimeOffset4Ms.parse("tuna")).toThrow();
expect(() => datetimeOffset4Ms.parse("1970-01-01T00:00:00.123Z")).toThrow();
expect(() =>
Expand Down
53 changes: 53 additions & 0 deletions deno/lib/benchmarks/datetime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Benchmark from "benchmark";

import { z } from "../index.ts";

const datetimeValidation = new Benchmark.Suite("datetime");
const DATA = "2020-01-01T00:00:00Z";
const datetimeRegex = z.datetimeRegex({});
console.log(datetimeRegex);
const MONTHS_31 = new Set([1, 3, 5, 7, 8, 10, 12]);
const MONTHS_30 = new Set([4, 6, 9, 11]);
datetimeValidation
.add("new Date()", () => {
const d = new Date(DATA);
return !isNaN(d.getTime());
})
.add("regex validation", () => {
return datetimeRegex.test(DATA);
})
.add("simple regex with validation", () => {
// Regex to validate ISO 8601 format with 'Z' timezone
const regex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/;
const match = DATA.match(regex);

if (!match) {
return false;
}

// Extract year, month, and day from the capture groups
const year = parseInt(match[1], 10);
const month = parseInt(match[2], 10); // month is 0-indexed in JavaScript Date, so subtract 1
const day = parseInt(match[3], 10);
if (month == 2) {
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
if (month === 2 && day > 29) {
return false;
}
}
}
if (MONTHS_30.has(month) && day > 30) {
return false;
}
if (MONTHS_31.has(month) && day > 31) {
return false;
}
return true;
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${datetimeValidation.name!}: ${e.target}`);
});

export default {
suites: [datetimeValidation],
};
8 changes: 6 additions & 2 deletions deno/lib/benchmarks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Benchmark from "benchmark";

import datetimeBenchmarks from "./datetime.ts";
import discriminatedUnionBenchmarks from "./discriminatedUnion.ts";
import objectBenchmarks from "./object.ts";
import primitiveBenchmarks from "./primitives.ts";
Expand Down Expand Up @@ -36,12 +37,15 @@ if (!argv.length) {
suites.push(...unionBenchmarks.suites);
}
if (argv.includes("--discriminatedUnion")) {
suites.push(...discriminatedUnionBenchmarks.suites);
suites.push(...datetimeBenchmarks.suites);
}
if (argv.includes("--datetime")) {
suites.push(...datetimeBenchmarks.suites);
}
}

for (const suite of suites) {
suite.run();
suite.run({});
}

// exit on Ctrl-C
Expand Down
4 changes: 2 additions & 2 deletions deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ function timeRegex(args: {
}

// Adapted from https://stackoverflow.com/a/3143231
function datetimeRegex(args: {
export function datetimeRegex(args: {
precision?: number | null;
offset?: boolean;
local?: boolean;
Expand All @@ -644,7 +644,7 @@ function datetimeRegex(args: {

const opts: string[] = [];
opts.push(args.local ? `Z?` : `Z`);
if (args.offset) opts.push(`([+-]\\d{2}(:?\\d{2})?)`);
if (args.offset) opts.push(`([+-]\\d{2}:?\\d{2})`);
regex = `${regex}(${opts.join("|")})`;
return new RegExp(`^${regex}$`);
}
Expand Down
46 changes: 46 additions & 0 deletions playground.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,49 @@
import { z } from "./src";

z;
// import { z } from "../index";

// const datetimeValidation = new Benchmark.Suite("datetime");
const DATA = "2020-01-01T00:00:00Z";
const datetimeRegex = z.datetimeRegex({});
console.log(datetimeRegex);

const MONTHS_31 = new Set([1, 3, 5, 7, 8, 10, 12]);
const MONTHS_30 = new Set([4, 6, 9, 11]);

{
const d = new Date(DATA);

console.log(!isNaN(d.getTime()));
}
{
console.log(datetimeRegex.test(DATA));
}
{
// Regex to validate ISO 8601 format with 'Z' timezone
const regex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/;
const match = DATA.match(regex);

if (!match) {
console.log(false);
}

// Extract year, month, and day from the capture groups
const year = parseInt(match![1], 10);
const month = parseInt(match![2], 10); // month is 0-indexed in JavaScript Date, so subtract 1
const day = parseInt(match![3], 10);
if (month == 2) {
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
if (month === 2 && day > 29) {
console.log(false);
}
}
}
if (MONTHS_30.has(month) && day > 30) {
console.log(false);
}
if (MONTHS_31.has(month) && day > 31) {
console.log(false);
}
console.log(true);
}
8 changes: 5 additions & 3 deletions src/__tests__/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ test("datetime parsing", () => {
datetimeOffset.parse("2020-10-14T17:42:29+00:00");
datetimeOffset.parse("2020-10-14T17:42:29+03:15");
datetimeOffset.parse("2020-10-14T17:42:29+0315");
datetimeOffset.parse("2020-10-14T17:42:29+03");
expect(() => datetimeOffset.parse("2020-10-14T17:42:29+03"));
expect(() => datetimeOffset.parse("tuna")).toThrow();
expect(() => datetimeOffset.parse("2022-10-13T09:52:31.Z")).toThrow();

Expand All @@ -524,7 +524,7 @@ test("datetime parsing", () => {
datetimeOffsetNoMs.parse("2022-10-13T09:52:31Z");
datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00:00");
datetimeOffsetNoMs.parse("2020-10-14T17:42:29+0000");
datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00");
expect(() => datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00")).toThrow();
expect(() => datetimeOffsetNoMs.parse("tuna")).toThrow();
expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00:00.000Z")).toThrow();
expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00:00.Z")).toThrow();
Expand All @@ -537,7 +537,9 @@ test("datetime parsing", () => {
datetimeOffset4Ms.parse("1970-01-01T00:00:00.1234Z");
datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00:00");
datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+0000");
datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00");
expect(() =>
datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00")
).toThrow();
expect(() => datetimeOffset4Ms.parse("tuna")).toThrow();
expect(() => datetimeOffset4Ms.parse("1970-01-01T00:00:00.123Z")).toThrow();
expect(() =>
Expand Down
53 changes: 53 additions & 0 deletions src/benchmarks/datetime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Benchmark from "benchmark";

import { z } from "../index";

const datetimeValidation = new Benchmark.Suite("datetime");
const DATA = "2020-01-01T00:00:00Z";
const datetimeRegex = z.datetimeRegex({});
console.log(datetimeRegex);
const MONTHS_31 = new Set([1, 3, 5, 7, 8, 10, 12]);
const MONTHS_30 = new Set([4, 6, 9, 11]);
datetimeValidation
.add("new Date()", () => {
const d = new Date(DATA);
return !isNaN(d.getTime());
})
.add("regex validation", () => {
return datetimeRegex.test(DATA);
})
.add("simple regex with validation", () => {
// Regex to validate ISO 8601 format with 'Z' timezone
const regex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/;
const match = DATA.match(regex);

if (!match) {
return false;
}

// Extract year, month, and day from the capture groups
const year = parseInt(match[1], 10);
const month = parseInt(match[2], 10); // month is 0-indexed in JavaScript Date, so subtract 1
const day = parseInt(match[3], 10);
if (month == 2) {
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
if (month === 2 && day > 29) {
return false;
}
}
}
if (MONTHS_30.has(month) && day > 30) {
return false;
}
if (MONTHS_31.has(month) && day > 31) {
return false;
}
return true;
})
.on("cycle", (e: Benchmark.Event) => {
console.log(`${datetimeValidation.name!}: ${e.target}`);
});

export default {
suites: [datetimeValidation],
};
8 changes: 6 additions & 2 deletions src/benchmarks/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Benchmark from "benchmark";

import datetimeBenchmarks from "./datetime";
import discriminatedUnionBenchmarks from "./discriminatedUnion";
import objectBenchmarks from "./object";
import primitiveBenchmarks from "./primitives";
Expand Down Expand Up @@ -36,12 +37,15 @@ if (!argv.length) {
suites.push(...unionBenchmarks.suites);
}
if (argv.includes("--discriminatedUnion")) {
suites.push(...discriminatedUnionBenchmarks.suites);
suites.push(...datetimeBenchmarks.suites);
}
if (argv.includes("--datetime")) {
suites.push(...datetimeBenchmarks.suites);
}
}

for (const suite of suites) {
suite.run();
suite.run({});
}

// exit on Ctrl-C
Expand Down
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,7 @@ function timeRegex(args: {
}

// Adapted from https://stackoverflow.com/a/3143231
function datetimeRegex(args: {
export function datetimeRegex(args: {
precision?: number | null;
offset?: boolean;
local?: boolean;
Expand All @@ -644,7 +644,7 @@ function datetimeRegex(args: {

const opts: string[] = [];
opts.push(args.local ? `Z?` : `Z`);
if (args.offset) opts.push(`([+-]\\d{2}(:?\\d{2})?)`);
if (args.offset) opts.push(`([+-]\\d{2}:?\\d{2})`);
regex = `${regex}(${opts.join("|")})`;
return new RegExp(`^${regex}$`);
}
Expand Down

0 comments on commit 35f0a38

Please sign in to comment.