5.1 KiB
5.1 KiB
OpenSpec Change: enterprise-datetime
- Change ID: enterprise-datetime
- Package:
@suaveplan/datetime - Category: universal
- Target path:
packages/universal/datetime - Status: IN PROGRESS
- Date: 2026-04-18
- Reference spec:
/mnt/s/web/suaveplan/enterprise/datetime.md
Summary
Greenfield implementation of @suaveplan/datetime for the genesis branch. The existing main-branch datetime package provides the baseline public-API surface to preserve; this change delivers timezone-aware date/time utilities with business-day arithmetic, calendar math, recurrence rules (RRule subset), relative time formatting, injectable clock, structured errors, property-based tests, OTel instrumentation, and benchmarks.
Current-state audit (genesis)
The package does not exist on genesis yet — this is a fresh build.
Available workspace deps on genesis
| Package | Available |
|---|---|
@suaveplan/error |
✅ |
@suaveplan/types |
✅ |
@suaveplan/events |
✅ |
@suaveplan/timing |
❌ not yet built — no dependency taken |
@suaveplan/streams |
❌ not yet built — not required |
@suaveplan/opentelemetry |
❌ not yet built — use @opentelemetry/api directly |
@suaveplan/feature-flags |
❌ not yet built — skip |
@suaveplan/schema-registry |
❌ not yet built — export Zod schemas directly |
External deps (via catalog):
zod→catalog:forms@opentelemetry/api→catalog:observabilitytslib→catalog:build
Dev deps (via catalog):
vitest,@vitest/coverage-istanbul→catalog:testingfast-check→catalog:testingmitata→catalog:testing
Preserved public API surface (from main branch)
All of the following must be exported from the genesis build:
Functions — Business Days
addBusinessDays(date, n, opts?)→DatebusinessDaysBetween(start, end, opts?)→numberisBusinessDay(date, opts?)→booleannextBusinessDay(date, opts?)→DatepreviousBusinessDay(date, opts?)→DatesubtractBusinessDays(date, n, opts?)→Date
Functions — Calendar
addDuration(date, duration)→DatedaysInMonth(year, month)→numberendOf(date, unit)→DategetCalendarMonth(year, month)→CalendarMonthisLeapYear(year)→booleanisSameDay(a, b)→booleanisSameMonth(a, b)→booleanstartOf(date, unit)→DatesubtractDuration(date, duration)→DatetoDate(input)→Date
Functions — Recurrence
expandRecurrence(rule, opts)→Date[]formatRRule(rule)→stringnextOccurrence(rule, from)→Date | nullparseRRule(input)→RecurrenceRule
Functions — Relative
diffInUnits(a, b, unit)→numberformatRelative(date, opts?)→stringtimeAgo(date, opts?)→stringtimeUntil(date, opts?)→string
Types
BusinessDayOptions—{ holidays?: Date[]; weekendDays?: Weekday[]; clock?: Clock }CalendarDay—{ date: Date; dayOfWeek: number; isWeekend: boolean; isToday: boolean }CalendarMonth—{ year: number; month: number; days: CalendarDay[] }DateInput—Date | string | numberDuration—{ years?: number; months?: number; weeks?: number; days?: number; hours?: number; minutes?: number; seconds?: number; milliseconds?: number }DurationUnit— union of duration field namesRelativeTimeOptions—{ locale?: string; clock?: Clock; precision?: DurationUnit }Timezone— IANA timezone string branded typeWeekday— 0–6 (Sunday–Saturday)DayAbbreviation— 'Sun' | 'Mon' | … | 'Sat'Frequency— 'daily' | 'weekly' | 'monthly' | 'yearly'RecurrenceOptions— options forexpandRecurrenceRecurrenceRule—{ freq: Frequency; interval?: number; count?: number; until?: Date; byDay?: Weekday[]; byMonthDay?: number[] }
New additions for genesis
Structured errors
DatetimeErrorextendsSuaveplanErrorwith codes underDATETIME.*namespace- Codes:
DATETIME.PARSE.INVALID_INPUT,DATETIME.RECURRENCE.INFINITE_EXPANSION,DATETIME.TIMEZONE.UNKNOWN
Zod schemas
DateInputSchema,DurationSchema,RecurrenceRuleSchema,TimezoneSchema— all public types with Zod schemas in*.schemas.tssiblings
Injectable clock
- All functions that read current time accept optional
{ clock?: Clock }— zero-config default usesDate.now()
OTel instrumentation
@opentelemetry/apicounters and histograms onexpandRecurrence,formatRelative, and business-day batch functions
Property-based tests (fast-check)
- Round-trip:
parseRRule → formatRRule → parseRRuleidentity - Inverse pairs:
addDuration + subtractDuration,addBusinessDays + subtractBusinessDays - Boundary invariants:
startOf(date, 'day')≤date≤endOf(date, 'day')
Benchmarks (mitata)
expandRecurrence1 000-occurrence daily rulegetCalendarMonth12-month batchbusinessDaysBetween10-year span
Impact
- Adds one new package:
packages/universal/datetime - Adds one new spec capability:
datetime - No existing package consumers on genesis yet