feat(parser): add ISO8601 duration parser
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
14
src/lib/server/parsers/iso8601-duration.ts
Normal file
14
src/lib/server/parsers/iso8601-duration.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
const PATTERN = /^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/;
|
||||||
|
|
||||||
|
export function parseIso8601Duration(input: string | null | undefined): number | null {
|
||||||
|
if (typeof input !== 'string' || input.length === 0) return null;
|
||||||
|
if (input === 'P' || input === 'PT') return null;
|
||||||
|
const m = PATTERN.exec(input);
|
||||||
|
if (!m) return null;
|
||||||
|
const [, d, h, min, s] = m;
|
||||||
|
if (!d && !h && !min && !s) return null;
|
||||||
|
const days = d ? parseInt(d, 10) : 0;
|
||||||
|
const hours = h ? parseInt(h, 10) : 0;
|
||||||
|
const mins = min ? parseInt(min, 10) : 0;
|
||||||
|
return days * 24 * 60 + hours * 60 + mins;
|
||||||
|
}
|
||||||
23
tests/unit/iso8601-duration.test.ts
Normal file
23
tests/unit/iso8601-duration.test.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { parseIso8601Duration } from '../../src/lib/server/parsers/iso8601-duration';
|
||||||
|
|
||||||
|
describe('parseIso8601Duration', () => {
|
||||||
|
it.each([
|
||||||
|
['PT30M', 30],
|
||||||
|
['PT1H', 60],
|
||||||
|
['PT1H30M', 90],
|
||||||
|
['PT2H15M', 135],
|
||||||
|
['PT0M', 0],
|
||||||
|
['P1DT2H', 26 * 60],
|
||||||
|
['PT90M', 90]
|
||||||
|
] as const)('parses %s to %i minutes', (input, expected) => {
|
||||||
|
expect(parseIso8601Duration(input)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([[''], [null], [undefined], ['garbage'], ['30 min'], ['PT']])(
|
||||||
|
'returns null for invalid input %j',
|
||||||
|
(input) => {
|
||||||
|
expect(parseIso8601Duration(input as string)).toBeNull();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user