Library Notes
Each library speaks one of the dialects but renders its own slice of it — adding tokens, omitting others, or gating some behind runtime configuration. Converting between libraries (rather than dialects) is what surfaces these differences; see Unsupported Tokens for how an unrenderable token is handled.
Localized presets
L… (Moment.js / Day.js) and P…/p… (date-fns) render per the library’s loaded locale.
rosetta-date maps them preset → preset (never to a concrete pattern), so the token stays
locale-deferred and the target library applies its own locale — including the compound connector
(" at ", ", ", …) it picks for that locale.
| Meaning | momentjs | dayjs | dateFns |
|---|---|---|---|
| Date, short | L | L | P |
| Date, medium | ll | ll | PP |
| Date, long | LL | LL | PPP |
| Time, short | LT | LT | p |
| Time, with seconds | LTS | LTS | pp |
| Date + time, medium | lll | lll | PPpp |
| Date + time, long | LLL | LLL | PPPppp |
| Date + time, full | LLLL | LLLL | PPPPpppp |
Day.js renders these through its LocalizedFormat plugin.
Locale caveat: conversion rewrites the token, not the date. Matching output needs equivalent
locales loaded in both libraries; the conversion only guarantees the token stays the locale’s
preset, never a hardcoded pattern. Samples are en-US — e.g. LL vs PPP differ only as
June 7, 2024 vs June 7th, 2024, which is each library’s en locale data, not a conversion error.
Match widths: for date + time, use a matched compound preset (PPpp, PPPppp) or a separator
(PP p). moment has no mismatched-width compound, so gluing different widths (PPPp) drops the
locale connector and can re-lex into a different token — rosetta-date maps each preset
faithfully, but a glued mismatched-width input was never valid to begin with.
momentjs
The reference implementation of the moment grammar. It renders every token in the
Token Mapping tables, so momentjs carries no supports set and
converting to it never flags a grammar token. Its localized L… presets map to date-fns P…; see
Localized presets.
dayjs
Day.js speaks the moment grammar but implements only a subset, split across its core formatter and
two common plugins. Tokens it does not recognize are mis-rendered at format time rather than
rejected, so converting to dayjs routes them through
onUnsupportedToken (reason unsupported-by-target) instead of
emitting something broken.
Tokens Day.js does not render (and therefore flags):
| Token(s) | Meaning |
|---|---|
N NN NNN NNNN NNNNN | Era |
Mo Qo Wo DDDo | Ordinals beyond Do / wo |
DDD DDDD | Day of year |
e E | Weekday number (locale / ISO) |
S SS | Sub-SSS fractional second |
gg GG | Week-numbering year, 2-digit |
Some tokens Day.js renders only with a plugin loaded — Q, Do, w / wo / ww, W / WW,
k / kk, gggg / GGGG, X, x, z need AdvancedFormat; the L… presets need
LocalizedFormat. rosetta-date emits these as-is; whether the plugin is actually loaded is the
consumer’s concern.
dateFns
date-fns is the reference implementation of the ldml (UTS #35) grammar plus its own
extensions — ISO week fields, the Unix epoch, and localized P…/p… presets. These extras
convert only through the dateFns library (from: momentjs, to: dateFns); the bare ldml dialect
does not define them, so a dialect → dialect conversion literalizes them instead.
| Meaning | momentjs | dayjs | dateFns |
|---|---|---|---|
| ISO week-numbering year | GGGG | GGGG | RRRR |
| ISO week-numbering year, 2-digit | GG | — | RR |
| ISO week of year | W | W | I |
| ISO week of year, 2-digit | WW | WW | II |
| ISO week of year, ordinal | Wo | — | Io |
| ISO weekday, number | E | — | i |
| Unix timestamp, seconds | X | X | t |
| Unix timestamp, milliseconds | x | x | T |
— marks a date-fns extension Day.js has no token for; converting it to dayjs flags it. The
ones Day.js does render here come from its AdvancedFormat plugin.
Option-gated tokens
date-fns guards a few tokens behind options you must enable in its format() call:
- Day of year (
D,DD) requiresuseAdditionalDayOfYearTokens: true. - Local week-year (
YY,YYYY) requiresuseAdditionalWeekYearTokens: true.
rosetta-date produces the standards-correct token; enabling these options is the caller’s
responsibility. The converter does not track which options you enabled — if a token needs one you
did not pass, date-fns throws at format() time, which is the authoritative signal for your exact
version.
Extensions with no moment counterpart
These date-fns extensions have no moment token, so converting them to moment produces an
escaped literal rather than a wrong guess:
dateFns | Meaning |
|---|---|
PPPP | Localized full date (with weekday) |
ppp pppp | Localized time with time zone |
Pp | Localized short date + time |