diff --git a/src/lib/server/parsers/iso8601-duration.ts b/src/lib/server/parsers/iso8601-duration.ts new file mode 100644 index 0000000..1bf8869 --- /dev/null +++ b/src/lib/server/parsers/iso8601-duration.ts @@ -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; +} diff --git a/tests/unit/iso8601-duration.test.ts b/tests/unit/iso8601-duration.test.ts new file mode 100644 index 0000000..c8989e1 --- /dev/null +++ b/tests/unit/iso8601-duration.test.ts @@ -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(); + } + ); +});