# @suaveplan/i18n-formatter-date - Intl.DateTimeFormat wrapper for the @suaveplan/i18n family — 8-preset table (short/medium/long/full/time-short/time-long/date-short/date-long), formatDate / formatTime / formatDateOnly / formatDateRange / getDayNames / getMonthNames. Per-(locale, options-hash) cache, Zod-validated options, OTel histogram + cache-hit counter, structured I18nError mapping. Universal leaf package. ## Summary Wrapper around `Intl.DateTimeFormat` for the `@suaveplan/i18n` family — 8-preset table (short/medium/long/full × date+time/time-only/date-only), range rendering, day/month name lists, and a per-(locale, options-hash) cache. ## Documentation tree - [day-month-names](./src/day-month-names/day-month-names.md) — `getDayNames(locale, style?)` and `getMonthNames(locale, style?)` return the locale-correct list of weekday or month na… - [day-month-names](./src/day-month-names/day-month-names.md) — `getDayNames(locale, style?)` and `getMonthNames(locale, style?)` return the locale-correct list of weekday or month na… - [formatter](./src/formatter/formatter.md) — `DateFormatter` and the `formatDate` / `formatDateRange` standalone helpers expose `Intl.DateTimeFormat` for the `@suav… - [formatter](./src/formatter/formatter.md) — `DateFormatter` and the `formatDate` / `formatDateRange` standalone helpers expose `Intl.DateTimeFormat` for the `@suav… - [helpers](./src/helpers/helpers.md) — `formatTime` and `formatDateOnly` pre-bind `formatDate` with the appropriate preset for time-only or date-only renderin… - [helpers](./src/helpers/helpers.md) — `formatTime` and `formatDateOnly` pre-bind `formatDate` with the appropriate preset for time-only or date-only renderin… - [observability](./src/observability/observability.md) — The Date formatter exports two OpenTelemetry instruments and a small in-memory state ring so spec-conformance tests can… - [observability](./src/observability/observability.md) — The Date formatter exports two OpenTelemetry instruments and a small in-memory state ring so spec-conformance tests can… - [presets](./src/presets/presets.md) — `PRESET_MAP` is the canonical 8-entry table that maps each `DateFormatPreset` discriminant to a concrete `Intl.DateTime… - [presets](./src/presets/presets.md) — `PRESET_MAP` is the canonical 8-entry table that maps each `DateFormatPreset` discriminant to a concrete `Intl.DateTime… ## Why this exists The 14-package i18n modular split (cross-cutting design `2026-05-05-i18n-modular-split.md`) lifts each `Intl.*` wrapper out of the monolith into its own sibling package. Lifting the date helper out of `@suaveplan/i18n` lets consumers that only need date/time rendering (most UI consumers) pull a single small package instead of the entire i18n core. The package adds the canonical Wave-3 formatter shape on top of the monolith's standalone helpers: Zod-validated options, a per-(locale, options-hash) cache, OTel histogram + cache-hit counter, and the `DateFormatter` class wrapper for symmetry with `NumberFormatter` / `RelativeTimeFormatter` / `ListFormatter` / `DisplayNameFormatter`. ## Install ```bash bun add @suaveplan/i18n-formatter-date ``` ## Quick Start ```ts import { DateFormatter, formatDate, formatDateOnly, formatDateRange, formatTime, getDayNames, getMonthNames, } from "@suaveplan/i18n-formatter-date"; const now = new Date(2026, 0, 1, 14, 30, 45); formatDate(now, "en-GB", { preset: "short" }); // "01/01/2026, 14:30" formatTime(now, "en-GB", true); // "14:30:45" formatDateOnly(now, "en-GB", true); // "1 January 2026" formatDateRange( new Date(2026, 0, 1), new Date(2026, 0, 5), "en-GB", { day: "numeric", month: "long" } ); // "1 – 5 January" getDayNames("en-GB", "short"); // ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] const f = new DateFormatter({ preset: "long", timeZone: "Europe/London" }); f.format(now, "en-GB"); ``` ## Modules | Subpath | Description | |---|---| | `@suaveplan/i18n-formatter-date` | Root barrel — class + standalones + helpers + presets + day/month names | | `@suaveplan/i18n-formatter-date/formatter` | `DateFormatter`, `formatDate`, `formatDateRange`, `toBcp47`, types, schema | | `@suaveplan/i18n-formatter-date/helpers` | `formatTime`, `formatDateOnly` | | `@suaveplan/i18n-formatter-date/presets` | `PRESET_MAP`, `DATE_FORMAT_PRESETS`, `resolvePreset` | | `@suaveplan/i18n-formatter-date/day-month-names` | `getDayNames`, `getMonthNames` | ## Modules Documentation - [formatter.md](src/formatter.md) — `DateFormatter`, `formatDate`, `formatDateRange`, `toBcp47`, `clearDateFormatterCache` - [helpers.md](src/helpers.md) — `formatTime`, `formatDateOnly` - [presets.md](src/presets.md) — 8-preset table + `resolvePreset` - [day-month-names.md](src/day-month-names.md) — `getDayNames`, `getMonthNames` - [observability.md](src/observability.md) — OTel histogram + cache-hit counter ## Public Surface (snapshot) | Export | Kind | Origin | |---|---|---| | `DateFormatter` | class | `formatter.ts` | | `formatDate` | function | `formatter.ts` | | `formatDateRange` | function | `formatter.ts` | | `toBcp47` | function | `formatter.ts` | | `clearDateFormatterCache` | function | `formatter.ts` | | `DateFormatterOptionsSchema` | Zod schema | `formatter.schemas.ts` | | `DateFormatPreset` | type | `formatter.types.ts` | | `DateFormatOptions` | type | `formatter.types.ts` | | `ExtendedDateOptions` | type | `formatter.types.ts` | | `DateFormatterCacheKey` | type | `formatter.types.ts` | | `formatTime` | function | `helpers.ts` | | `formatDateOnly` | function | `helpers.ts` | | `PRESET_MAP` | const | `presets.ts` | | `DATE_FORMAT_PRESETS` | const | `presets.ts` | | `resolvePreset` | function | `presets.ts` | | `PresetTable` | type | `presets.types.ts` | | `getDayNames` | function | `day-month-names.ts` | | `getMonthNames` | function | `day-month-names.ts` | | `recordDateFormat` | function | `observability.ts` | | `recordDateCacheHit` | function | `observability.ts` | | `getDateMetricsState` | function | `observability.ts` | | `resetDateMetricsState` | function | `observability.ts` | ## Versioning Part of the 14-package i18n modular-split rollout. Stays at `0.1.0` until the slim core (Wave 6) cuts the family `1.0.0` release.