toLocaleString()

ES1+

Returns a language-sensitive string representation of the date and time, formatted according to the given locale and options, or the environment's defaults when omitted. It is the display-oriented counterpart to toISOString(), backed by the same CLDR locale data as Intl.DateTimeFormat.

Syntax

date.toLocaleString(locales, options)

Parameters

locales string | Array optional

A string with a BCP 47 language tag, or an array of such strings

options Object optional

An object with configuration properties

Return Value

string

A string representing the date

Examples

JavaScript
const date = new Date('2024-01-15T14:30:00');
console.log(date.toLocaleString('ko-KR'));
console.log(date.toLocaleString('en-US'));
console.log(date.toLocaleString('ja-JP'));
Output:
// '2024. 1. 15. 오후 2:30:00' '1/15/2024, 2:30:00 PM' '2024/1/15 14:30:00'

📌 When to Use

Use toLocaleString() whenever a date-time is destined for human eyes: timestamps on comments and messages, order confirmation screens, dashboards, receipts, and anywhere else users expect dates in their own conventions. Locale-aware formatting is not cosmetic; "03/04/2025" means March 4 to an American and April 3 to most Europeans, so hand-rolled formats actively misinform part of an international audience. The options object is the real power: dateStyle and timeStyle presets give idiomatic output in one line, while component options (year, month, day, hour, minute, weekday) compose custom layouts, and the timeZone option formats any instant in any IANA zone, which is the correct way to show "this event happens at 9 AM Seoul time" to a viewer in Berlin. Prefer passing an explicit locale (usually the user's chosen language, falling back to navigator.language) over relying on the runtime default, which on servers is often nothing like your users' locale. For repeated formatting of many dates, construct one Intl.DateTimeFormat and reuse it; toLocaleString() with options builds equivalent machinery on every call. Never feed its output back into date parsing or comparisons; it is presentation text whose exact form legitimately varies between engines and ICU versions.

⚠️ Common Mistakes

Omitting the locale argument and shipping whatever the runtime defaults to. On servers and CI containers the default is frequently en-US or the POSIX C locale regardless of your users, and server-rendered HTML then mismatches client re-renders, producing hydration warnings in SvelteKit, React, and friends.

Parsing or string-comparing formatted output. The exact text is not part of any spec: ICU updates have changed separators, inserted U+202F narrow no-break space before AM/PM, and adjusted era names, breaking tests and parsers that assumed stable output. Compare timestamps, not strings; snapshot-test with pinned Node/ICU only.

Storing toLocaleString() output in databases or sending it between services. Locale text is lossy (no offset, ambiguous order) and cannot be reliably re-parsed; persistence belongs to toISOString() or epoch milliseconds, with locale formatting applied at render time.

Mixing dateStyle/timeStyle presets with component options like hour or month: the spec makes this combination throw a TypeError at runtime, a failure that only surfaces on the code path using the bad options object.

Ignoring the timeZone option and letting every viewer see the event in their own zone when the product means a specific place's wall time ("doors open 19:00 in Tokyo"); explicit timeZone plus timeZoneName renders the intended clock unambiguously.

Rebuilding formatters in loops or list renders: date.toLocaleString("de-DE", options) inside a 500-row table instantiates locale machinery 500 times and can add visible jank; a hoisted Intl.DateTimeFormat.format is the cheap path.

✅ Best Practices

Always pass locale and options explicitly: toLocaleString(userLocale, { dateStyle: "medium", timeStyle: "short" }). Deterministic inputs make output reproducible across environments and reviewable in code.

Hoist Intl.DateTimeFormat instances for any repeated formatting, memoized per locale-plus-options combination; format() calls against a cached formatter are an order of magnitude cheaper than toLocaleString() with options.

Render instants in a specific zone with the timeZone option ({ timeZone: "Asia/Seoul", timeZoneName: "short" }) instead of shifting the underlying timestamp, which corrupts the instant to make the text look right.

Keep formatting at the presentation edge: store ISO/epoch values in state and format in the component layer, so the same datum can appear as "3 minutes ago" in one widget and a full timestamp in another (Intl.RelativeTimeFormat handles the former).

In SSR frameworks, format with the user's negotiated locale and an explicit timeZone on both server and client (or defer formatting to the client) to avoid hydration mismatches from environment-dependent defaults.

Explore Intl.DateTimeFormat's relatives before writing custom logic: formatRange() for "Jan 3-5", RelativeTimeFormat for "yesterday", and Temporal's toLocaleString(), which shares this options vocabulary for its immutable types.

⚡ Performance Notes

toLocaleString() is by far the most expensive Date method: with options it effectively constructs an Intl.DateTimeFormat, which involves locale negotiation and loading CLDR pattern data, then formats through ICU. A single call costs microseconds, harmless in isolation, but in a list of hundreds of rows or a per-frame update it becomes the dominant date-related cost, often 50-100x slower than toISOString(). The remedy is caching: create Intl.DateTimeFormat once per locale-options pair and call format(date) repeatedly; engines also maintain internal formatter caches for repeated identical toLocaleString() arguments, but explicit reuse is more reliable across engines. formatToParts() costs about the same as format() and returns structured tokens, letting you wrap components in markup without re-parsing the formatted string. Locale data also affects bundle-free memory, not download size, since ICU ships with the engine.

🌍 Real World Example

Multilingual Date Display

A multilingual timestamp component is where toLocaleString() proves its worth: one stored UTC instant renders as native-feeling text for every user, Korean date order and 12-hour markers for ko-KR, day-first for de-DE, and so on, purely by switching the locale argument. The example wires the locale to user settings with a sensible navigator.language fallback and explicit options for deterministic output. This exact pattern, one canonical timestamp plus per-viewer formatting, is how chat apps, banking dashboards, and booking systems present the same moment correctly worldwide.

function formatEventDate(date, locale, options = {}) {
  const defaultOptions = {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit'
  };

  const mergedOptions = { ...defaultOptions, ...options };
  const eventDate = new Date(date);

  return {
    formatted: eventDate.toLocaleString(locale, mergedOptions),
    relative: getRelativeTime(eventDate, locale)
  };
}

function getRelativeTime(date, locale) {
  const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
  const diff = date - new Date();
  const days = Math.round(diff / (1000 * 60 * 60 * 24));
  return rtf.format(days, 'day');
}

console.log(formatEventDate('2024-03-20T14:00:00', 'ko-KR'));
// { formatted: '2024년 3월 20일 수요일 오후 02:00', relative: '5일 후' }

Related Methods