#1802·luxon

Share `Locale` instances between DateTimes with the same configuration

Author: gespositoCreated Sep 14, 2026Updated Sep 15, 2026

Is your feature request related to a problem? Please describe.

Every DateTime and Duration owns its own Locale, because Locale.create returns a fresh instance on every call. A Locale retains about 480 bytes in Node (its five lazy caches, the Intl config string and the instance itself), so a DateTime that is nothing more than a timestamp costs about 680 bytes retained, and roughly 70% of that is the Locale. In a heap snapshot of a large single-page application, about 24,800 retained DateTimes came with about 24,800 Locales, 13.6 MB in total, even though only a handful of distinct locale configurations existed.

The per-instance lazy caches have a CPU cost too: dt.weekdayLong, dt.monthLong, Info.months(..., { locale }) and friends rebuild their name lists through Intl.DateTimeFormat for every new DateTime in a non-English locale, because each instance starts with empty caches.

Describe the solution you'd like

Memoize Locale.create on the resolved configuration (locale, numbering system, output calendar, week settings, and whether the locale was specified or fell back), the way IANAZone.create already shares zones (#923), and clear it from Settings.resetCaches() with the other caches.

This is safe because a Locale is never mutated after construction (every change goes through clone(), which builds a new one) and its lazily populated lists depend only on its configuration. Two consequences of sharing need handling in the same change:

  • Deep-freezing a DateTime also freezes its Locale (#1104). With sharing, a frozen cached instance would break every other DateTime with that configuration, so the cache should treat a non-extensible instance as a miss and replace it.
  • The Info listers currently hand out the Locale's own cached arrays, which callers may mutate. With sharing they need to return copies, as the English lists and Info.getWeekendWeekdays already do.

Measured on Node 26: retained heap per DateTime goes from 680 to 208 bytes, one Locale instead of 50,000 for 50,000 DateTimes, creation and formatting paths a few percent faster, and fresh-instance name lookups in non-English locales one to two orders of magnitude faster. No path gets slower outside the noise floor. I have a patch with tests ready and will open it as a PR referencing this issue.

Describe alternatives you've considered

  • Doing nothing: callers cannot fix this from outside, since Locale.create is internal and every factory goes through it.
  • Keying the cache on a composite string, as the Intl caches do: measured, and it regresses DateTime.now() by 17% to 25% because building and hashing a fresh key on every creation costs more than the allocation it saves. Grouping entries by locale string and comparing the remaining fields directly avoids that.
  • An unbounded cache, matching luxon's other caches: workable, but a bound is cheap insurance for callers that pass unbounded distinct locale strings or weekSettings values. Happy to go either way.
  • An opt-in or opt-out setting: the other caches have none, and this would add API surface for no clear benefit.

Additional context

Byte figures are for V8 without pointer compression (Node); Chrome stores roughly half. Related: #923 (IANAZone cache), #1104 (frozen Duration), #1642.