3.2 KiB
datetime
Purpose
Timezone-aware date/time utilities with business-day arithmetic, recurrence rules, calendar math, and relative time formatting. Provides custom-holiday-aware business-day functions, RRule subset (FREQ/INTERVAL/COUNT/UNTIL/BYDAY/BYMONTHDAY), and i18n-keyed relative-time output, all with an injectable Clock so tests run deterministically.
Requirements
DT-01 — DateInput acceptance
Every function that accepts a date parameter MUST accept DateInput = Date | string | number. Strings must be ISO-8601; numbers are Unix milliseconds. Invalid inputs throw DatetimeError with code DATETIME.PARSE.INVALID_INPUT.
DT-02 — Injectable clock
Every function that reads current time (e.g. timeAgo, isBusinessDay for "today") MUST accept an optional { clock?: Clock } option. The Clock interface is sourced from @suaveplan/types. Zero-config default uses Date.now(). No Date.now() or new Date() calls at module scope.
DT-03 — Business day arithmetic
addBusinessDays, subtractBusinessDays, nextBusinessDay, previousBusinessDay, isBusinessDay, businessDaysBetween MUST all accept BusinessDayOptions including a custom holidays?: Date[] list and weekendDays?: Weekday[] array (defaults: Saturday and Sunday). businessDaysBetween MUST check AbortSignal every 256 days to support large ranges.
DT-04 — Calendar math correctness
addDuration and subtractDuration MUST correctly handle month rollover (e.g. Jan 31 + 1 month = Feb 28/29) and DST transitions without double-counting hours. daysInMonth MUST return correct values for all months including February in leap years.
DT-05 — Recurrence rules
parseRRule MUST parse a simplified RRULE string supporting FREQ, INTERVAL, COUNT, UNTIL, BYDAY, BYMONTHDAY. expandRecurrence MUST require either count or until in the rule; rules missing both MUST throw DatetimeError with code DATETIME.RECURRENCE.INFINITE_EXPANSION. AbortSignal is checked every 64 occurrences.
DT-06 — Structured errors
All thrown errors MUST extend DatetimeError which extends SuaveplanError. Error codes are namespaced under DATETIME.*. The error includes { code, message, cause?, context? }.
DT-07 — Zod schemas
Every exported public type that carries runtime data MUST have a corresponding Zod schema in a sibling *.schemas.ts file. Schemas are exported directly (not via a schema-registry package).
DT-08 — i18n-ready output
formatRelative, timeAgo, and timeUntil MUST produce output via i18n-keyed strings. The default locale is English pass-through. The key system is compatible with a future @suaveplan/i18n adapter.
DT-09 — OTel instrumentation
expandRecurrence and businessDaysBetween MUST emit OpenTelemetry histograms tracking call duration and output size. All instruments are registered via @opentelemetry/api and are no-ops when no exporter is registered.
DT-10 — sideEffects: false
The package MUST set "sideEffects": false in package.json. A tree-shake test MUST verify no side effects leak at import time.
DT-11 — Coverage gate
Vitest thresholds MUST be set to 100/100/100/100 (statement/branch/function/line). No istanbul ignore pragmas. No .skip or .only in src/.