Skip to content

Commit

Permalink
Update Duration to better support ISO 8601-2
Browse files Browse the repository at this point in the history
Use a better regex to support negatives and decimal fractions to the smallest value.
Add a lot of valid and invalid inputs to the test.
  • Loading branch information
mastermatt committed Feb 25, 2024
1 parent 80d41e8 commit 49d0ebd
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 10 deletions.
49 changes: 45 additions & 4 deletions deno/lib/__tests__/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,11 +474,52 @@ test("duration", () => {
const duration = z.string().duration();
expect(duration.isDuration).toEqual(true);

duration.parse("P3Y6M4DT12H30M5S");
const validDurations = [
"P3Y6M4DT12H30M5S",
"P2Y9M3DT12H31M8.001S",
"+P3Y6M4DT12H30M5S",
"-PT0.001S",
"+PT0.001S",
"PT0,001S",
"PT12H30M5S",
"-P2M1D",
"P-2M-1D",
"-P5DT10H",
"P-5DT-10H",
"P1Y",
"P2MT30M",
"PT6H",
"P5W",
"P0.5Y",
"P0,5Y",
"P42YT7.004M",
];

const invalidDurations = [
"foo bar",
"",
" ",
"P",
"T1H",
"P0.5Y1D",
"P0,5Y6M",
"P1YT",
];

for (const val of validDurations) {
const result = duration.safeParse(val);
if (!result.success) {
throw Error(`Valid duration could not be parsed: ${val}`);
}
}

for (const val of invalidDurations) {
const result = duration.safeParse(val);

if (result.success) {
throw Error(`Invalid duration was successful parsed: ${val}`);
}

const result = duration.safeParse("invalidDuration");
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues[0].message).toEqual("Invalid duration");
}
});
Expand Down
2 changes: 1 addition & 1 deletion deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ const ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/;
const uuidRegex =
/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i;
const durationRegex =
/^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/;
/^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/;

// from https://stackoverflow.com/a/46181/1550155
// old version: too slow, didn't support unicode
Expand Down
49 changes: 45 additions & 4 deletions src/__tests__/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,11 +473,52 @@ test("duration", () => {
const duration = z.string().duration();
expect(duration.isDuration).toEqual(true);

duration.parse("P3Y6M4DT12H30M5S");
const validDurations = [
"P3Y6M4DT12H30M5S",
"P2Y9M3DT12H31M8.001S",
"+P3Y6M4DT12H30M5S",
"-PT0.001S",
"+PT0.001S",
"PT0,001S",
"PT12H30M5S",
"-P2M1D",
"P-2M-1D",
"-P5DT10H",
"P-5DT-10H",
"P1Y",
"P2MT30M",
"PT6H",
"P5W",
"P0.5Y",
"P0,5Y",
"P42YT7.004M",
];

const invalidDurations = [
"foo bar",
"",
" ",
"P",
"T1H",
"P0.5Y1D",
"P0,5Y6M",
"P1YT",
];

for (const val of validDurations) {
const result = duration.safeParse(val);
if (!result.success) {
throw Error(`Valid duration could not be parsed: ${val}`);
}
}

for (const val of invalidDurations) {
const result = duration.safeParse(val);

if (result.success) {
throw Error(`Invalid duration was successful parsed: ${val}`);
}

const result = duration.safeParse("invalidDuration");
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues[0].message).toEqual("Invalid duration");
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ const ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/;
const uuidRegex =
/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i;
const durationRegex =
/^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/;
/^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/;

// from https://stackoverflow.com/a/46181/1550155
// old version: too slow, didn't support unicode
Expand Down

0 comments on commit 49d0ebd

Please sign in to comment.