Date.prototype.toLocaleDateString, toLocaleTimeString and toLocaleString

A full Intl.DateTimeFormat behind three method calls. Every hand-rolled date format is an attempt to reimplement part of this, usually for one locale only.

Date methodsES3 (options since ES2012)Live demo
Common call
d.toLocaleDateString('en-GB')
Returns
a formatted string
Replaces
manual getMonth + 1 formatting
Watch out
the default locale AND timezone come from the runtime
date.toLocaleDateString([locales[, options]])
→ string

Demo

Live evaluation
Try:
Inputs
isostringan ISO date
localestringe.g. en-US, de-DE
Output
new Date('2026-03-15T12:00:00Z').toLocaleDateString('en-US', { timeZone: 'UTC' })
'3/15/2026'

The same instant, five conventions. en-US puts the month first and en-GB the day, which is precisely the ambiguity that makes 03/04 unreadable without knowing the locale — and why hand-rolling a slash format is always wrong for somebody. The sv-SE case is a useful trick: Swedish convention happens to be ISO-like, so it is the shortest way to get a LOCAL calendar date in YYYY-MM-DD form. Note the demo pins timeZone to UTC; without that, the output would also depend on where each reader is sitting.

Parameters

NameTypeRequiredDescription
localesstring | string[]no (host default)A BCP 47 tag such as "en-GB" or "de-DE". Omitted, the runtime locale is used — which differs between your machine and your users.
optionsobjectno ({})Intl.DateTimeFormat options — dateStyle, timeStyle, weekday, month, day, year, hour, minute, timeZone, hour12. Setting timeZone is what makes output reproducible.

Return value

string — The date formatted for the given locale. toLocaleDateString gives the date part, toLocaleTimeString the time part, toLocaleString both.

Common patterns

A readable date
dateStyle covers the common cases.
d.toLocaleDateString('en-GB', {dateStyle: 'long'});
Local calendar day in ISO form
The sv-SE trick, avoiding a UTC slice.
d.toLocaleDateString('sv-SE');   // '2026-03-15'
Reuse a formatter
Much faster across many dates.
const f = new Intl.DateTimeFormat('en-GB', {dateStyle: 'short'});
rows.map(r => f.format(r.at));

Examples

1. US order
new Date('2026-03-15T12:00:00Z').toLocaleDateString('en-US', {timeZone: 'UTC'})
Returns
'3/15/2026'
2. British order
new Date('2026-03-15T12:00:00Z').toLocaleDateString('en-GB', {timeZone: 'UTC'})
Returns
'15/03/2026'
3. German
new Date('2026-03-15T12:00:00Z').toLocaleDateString('de-DE', {timeZone: 'UTC'})
Returns
'15.3.2026'
4. Swedish is ISO-like
new Date('2026-03-15T12:00:00Z').toLocaleDateString('sv-SE', {timeZone: 'UTC'})
Returns
'2026-03-15'
5. A named timezone
new Date('2026-03-15T12:00:00Z').toLocaleString('en-GB', {timeZone: 'Asia/Tokyo'})
Returns
'15/03/2026, 21:00:00'
6. Invalid does not throw
new Date('nonsense').toLocaleDateString()
Returns
'Invalid Date'

Pitfalls

1. The default locale and timezone are the runtime
Omitting both means output depends on the machine — your laptop, your CI runner, your user browser. Snapshot tests fail for reasons that look random, and a server renders dates in whatever zone it was deployed to.
Environment-dependent
d.toLocaleDateString()
varies by machine
Pin both
d.toLocaleDateString('en-GB', {timeZone: 'UTC'})
reproducible
2. The output is not machine-parseable
It contains locale-specific separators, sometimes non-breaking spaces, and ordering that varies. Date.parse will not reliably read it back. Format only at the edge of your system and keep the Date or ISO string internally.
Round trip fails
Date.parse(new Date('2026-03-15T12:00:00Z').toLocaleDateString('de-DE'))
NaN
Keep ISO
const stored = d.toISOString();
parses reliably
3. It is slow in a loop
Each call may build a formatter from locale data. Rendering a table of thousands of dates this way is measurably slower than constructing one Intl.DateTimeFormat and reusing its format method.
Rebuilds each time
rows.map(r => r.at.toLocaleDateString("en-GB"))
fine for small lists
Reuse a formatter
const f = new Intl.DateTimeFormat("en-GB");
rows.map(r => f.format(r.at));
much faster
4. Two-digit years and 12-hour clocks surprise people
Short formats in some locales use a two-digit year, and en-US defaults to a 12-hour clock with AM/PM. If you need a specific shape, set the options explicitly rather than trusting the locale default.
Locale default
new Date('2026-03-15T13:00:00Z').toLocaleTimeString('en-US', {timeZone: 'UTC'})
'1:00:00 PM'
Force 24-hour
new Date('2026-03-15T13:00:00Z').toLocaleTimeString('en-GB', {timeZone: 'UTC', hour12: false})
'13:00:00'

When to use

Use it
  • Any date shown to a person
  • Rendering in a specific named timezone, via the timeZone option
  • Month and weekday names, without a lookup table
  • Getting a LOCAL calendar day, via the sv-SE trick
Reach for something else
  • Storing or transmitting → toISOString
  • Formatting many dates → Intl.DateTimeFormat, reused
  • Output you will parse back → keep the original
  • Reproducible tests → pin both locale and timeZone

Notes

Complexity
O(1) per call, with substantial constant cost from locale setup
Return
A new string; the Date is unchanged
CPython impl
V8: Builtins-date-tolocalestring / ICU
Memory
May allocate a formatter per call unless one is reused
Thread-safe
Single-threaded

FAQ

Use the sv-SE locale, whose convention is ISO-like. Slicing toISOString gives the UTC day, which can be a day out from the reader local date.

d.toLocaleDateString('sv-SE');      // local day
d.toISOString().slice(0, 10);       // UTC day

History

ES3
The three toLocale* methods added with implementation-defined output.
ES2012
ECMA-402 gave them locales and options, tying them to Intl.DateTimeFormat.
ES2020
dateStyle and timeStyle shorthands added.