Initial Commit
This commit is contained in:
135
openspec/archive/2026-04-19-enterprise-datetime/proposal.md
Normal file
135
openspec/archive/2026-04-19-enterprise-datetime/proposal.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# 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:observability`
|
||||
- `tslib` → `catalog:build`
|
||||
|
||||
Dev deps (via catalog):
|
||||
|
||||
- `vitest`, `@vitest/coverage-istanbul` → `catalog:testing`
|
||||
- `fast-check` → `catalog:testing`
|
||||
- `mitata` → `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?)` → `Date`
|
||||
- `businessDaysBetween(start, end, opts?)` → `number`
|
||||
- `isBusinessDay(date, opts?)` → `boolean`
|
||||
- `nextBusinessDay(date, opts?)` → `Date`
|
||||
- `previousBusinessDay(date, opts?)` → `Date`
|
||||
- `subtractBusinessDays(date, n, opts?)` → `Date`
|
||||
|
||||
### Functions — Calendar
|
||||
|
||||
- `addDuration(date, duration)` → `Date`
|
||||
- `daysInMonth(year, month)` → `number`
|
||||
- `endOf(date, unit)` → `Date`
|
||||
- `getCalendarMonth(year, month)` → `CalendarMonth`
|
||||
- `isLeapYear(year)` → `boolean`
|
||||
- `isSameDay(a, b)` → `boolean`
|
||||
- `isSameMonth(a, b)` → `boolean`
|
||||
- `startOf(date, unit)` → `Date`
|
||||
- `subtractDuration(date, duration)` → `Date`
|
||||
- `toDate(input)` → `Date`
|
||||
|
||||
### Functions — Recurrence
|
||||
|
||||
- `expandRecurrence(rule, opts)` → `Date[]`
|
||||
- `formatRRule(rule)` → `string`
|
||||
- `nextOccurrence(rule, from)` → `Date | null`
|
||||
- `parseRRule(input)` → `RecurrenceRule`
|
||||
|
||||
### Functions — Relative
|
||||
|
||||
- `diffInUnits(a, b, unit)` → `number`
|
||||
- `formatRelative(date, opts?)` → `string`
|
||||
- `timeAgo(date, opts?)` → `string`
|
||||
- `timeUntil(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 | number`
|
||||
- `Duration` — `{ years?: number; months?: number; weeks?: number; days?: number; hours?: number; minutes?: number; seconds?: number; milliseconds?: number }`
|
||||
- `DurationUnit` — union of duration field names
|
||||
- `RelativeTimeOptions` — `{ locale?: string; clock?: Clock; precision?: DurationUnit }`
|
||||
- `Timezone` — IANA timezone string branded type
|
||||
- `Weekday` — 0–6 (Sunday–Saturday)
|
||||
- `DayAbbreviation` — 'Sun' | 'Mon' | … | 'Sat'
|
||||
- `Frequency` — 'daily' | 'weekly' | 'monthly' | 'yearly'
|
||||
- `RecurrenceOptions` — options for `expandRecurrence`
|
||||
- `RecurrenceRule` — `{ freq: Frequency; interval?: number; count?: number; until?: Date; byDay?: Weekday[]; byMonthDay?: number[] }`
|
||||
|
||||
## New additions for genesis
|
||||
|
||||
### Structured errors
|
||||
|
||||
- `DatetimeError` extends `SuaveplanError` with codes under `DATETIME.*` 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.ts` siblings
|
||||
|
||||
### Injectable clock
|
||||
|
||||
- All functions that read current time accept optional `{ clock?: Clock }` — zero-config default uses `Date.now()`
|
||||
|
||||
### OTel instrumentation
|
||||
|
||||
- `@opentelemetry/api` counters and histograms on `expandRecurrence`, `formatRelative`, and business-day batch functions
|
||||
|
||||
### Property-based tests (fast-check)
|
||||
|
||||
- Round-trip: `parseRRule → formatRRule → parseRRule` identity
|
||||
- Inverse pairs: `addDuration + subtractDuration`, `addBusinessDays + subtractBusinessDays`
|
||||
- Boundary invariants: `startOf(date, 'day')` ≤ `date` ≤ `endOf(date, 'day')`
|
||||
|
||||
### Benchmarks (mitata)
|
||||
|
||||
- `expandRecurrence` 1 000-occurrence daily rule
|
||||
- `getCalendarMonth` 12-month batch
|
||||
- `businessDaysBetween` 10-year span
|
||||
|
||||
## Impact
|
||||
|
||||
- Adds one new package: `packages/universal/datetime`
|
||||
- Adds one new spec capability: `datetime`
|
||||
- No existing package consumers on genesis yet
|
||||
Reference in New Issue
Block a user