Intl Options
The adapter at rosetta-date/intl bridges token strings and the native
Intl.DateTimeFormat
API. It is the path from a hardcoded pattern to native, locale-aware formatting.
import { toIntlOptions } from 'rosetta-date/intl'
import { moment } from 'rosetta-date/dialects'
const options = toIntlOptions('YYYY-MM-DD HH:mm', { from: moment })
// { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hourCycle: 'h23' }
new Intl.DateTimeFormat('pt-BR', options).format(new Date())Why a separate surface
Every other endpoint is a token string. Intl.DateTimeFormat is not — it takes an unordered
options bag and lets the locale decide layout at format time:
| Model | Carries order? | Carries separators / literals? | Examples |
|---|---|---|---|
| Pattern | yes | yes | DD/MM/YYYY, every dialect and library |
| Field bag | no | no | Intl options |
Crossing that boundary is a deliberate, lossy projection — so the adapter is its own pair of
functions rather than another endpoint for convert.
toIntlOptions — pattern → options
toIntlOptions(format, { from }) reads the locale-agnostic intent out of a format string.
import { toIntlOptions } from 'rosetta-date/intl'
import { moment } from 'rosetta-date/dialects'
toIntlOptions('h:mm A', { from: moment })
// { hour: 'numeric', minute: '2-digit', hourCycle: 'h12' }Literals and field order are dropped by design — the locale, not the format, decides where the
slashes go and whether the day leads. 'DD/MM/YYYY' and 'YYYY-MM-DD' produce the same options;
hand them to a pt-BR formatter and you get the Brazilian layout back, to an en-US one the American
layout.
The AM/PM markers — uppercase A and lowercase a — have no Intl option, so on their own they are
dropped (or throw under 'throw'). In practice the marker is redundant: an h11 / h12 hour
already carries the 12-hour cycle via hourCycle, so dropping it loses nothing.
fromIntlOptions — options → pattern
fromIntlOptions(intlOptions, { to }) goes the other way. Its locale-correct path is the style
axis: dateStyle / timeStyle map to the target’s localized presets, which the library resolves
per locale at format time — so no locale argument is needed.
import { fromIntlOptions } from 'rosetta-date/intl'
import { dayjs } from 'rosetta-date/libraries'
fromIntlOptions({ dateStyle: 'short' }, { to: dayjs }) // 'L'
fromIntlOptions({ dateStyle: 'long', timeStyle: 'long' }, { to: dayjs }) // 'LLL'Targeting a library returns its preset (e.g. L for Moment.js / Day.js, P for date-fns) precisely
because that preset stays locale-aware — freezing it to a fixed pattern would defeat the library’s own
locale machinery. See Localized presets.
Components reconstruct a skeleton
When the options are a component bag rather than a style, there is no order or separator to recover without a locale, so the output is a skeleton — the fields in CLDR canonical order, no separators:
fromIntlOptions({ year: 'numeric', month: '2-digit', day: '2-digit' }, { to: ldml }) // 'yyyyMMdd'A round trip preserves the field set, not the layout: toIntlOptions then fromIntlOptions
recovers the components, but the slashes and ordering are gone — they live in the locale, not the
format.
Unsupported fields
The canonical vocabulary is a strict superset of what Intl.DateTimeFormat can express. Fields with
no equivalent are routed to onUnsupportedToken:
toIntlOptions— quarter, week-numbering year, week of year, day-of-year, ordinals, epoch, the AM/PM marker, stand-alone names, and the numeric / short weekday forms.fromIntlOptions— a preset against a pure dialect that has no preset token, adateStyle/timeStylemismatch with no single fused preset, an Intl-only zone style (long,*Generic), and the flexibledayPeriod.
The policy is narrower than the dialect-to-dialect one: only 'throw' and 'drop' (the default)
apply, since there is no output string to literalize and no target dialect for a handler.
toIntlOptions('[Q]Q YYYY', { from: moment }) // { year: 'numeric' } — quarter dropped
toIntlOptions('Q', { from: moment, onUnsupportedToken: 'throw' }) // throws UnsupportedTokenErrortoIntlOptions is a human-display tool: it yields options for Intl.DateTimeFormat, which renders
a GMT-prefixed offset (GMT+05:00), not a bare ISO +05:00. The ISO offsets that emit Z at zero
have no Intl equivalent at all. Keep machine-readable ISO timestamps as a plain convert between
dialects.