Format a Date Range with Intl.DateTimeFormat

To format a date range, use formatter.formatRange(start, end) — it collapses shared fields ('Mar 3 - 5, 2024') and uses the locale's range separator. Part of Mastering Intl.DateTimeFormat Options.

Why this scenario is tricky

Concatenating two formatted dates with a hyphen ('Mar 3, 2024' + ' - ' + 'Mar 5, 2024') is verbose and wrong in most locales: it repeats the shared month and year and uses the wrong dash. formatRange knows to collapse common parts and pick the locale's range dash.

Ranges are more than two dates joinedConcatenation repeats shared fieldsRanges are more than two dates joinedfmt(a) + ' - ' + fmt(b)'Mar 3, 2024 - Mar 5, 2024'fmt.formatRange(a, b)'Mar 3 - 5, 2024'formatRange collapses shared month/year and uses the locale separator.

Minimal working solution

const fmt = new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
fmt.formatRange(new Date('2024-03-03'), new Date('2024-03-05')); // 'Mar 3 - 5, 2024'

formatRangeOne call renders the whole spanformatRangestart, endformatRangecollapsed span

Full production version

// Cache the formatter; formatRangeToParts gives you the pieces for custom markup.
const fmt = new Intl.DateTimeFormat('de-DE', { dateStyle: 'medium' });
const parts = fmt.formatRangeToParts(new Date('2024-03-03'), new Date('2024-03-05'));
// each part is tagged source: 'startRange' | 'endRange' | 'shared'

formatRangeToPartsTagged pieces for custom renderingformatRangeToPartsstart,endformatRangeToPartstagged partscustom markup

Verification snippet

Format a Date Range assertionsKey cases assert correctlyAssertions that prove the edge caseen-US same month'Mar 3 - 5, 2024'cross-month'Mar 30 - Apr 2'de-DElocale dashequal datessingle date

Common pitfalls

Format a Date Range pitfallsCommon mistakes and their fixesWrongRightconcatenate two formatsrepeats fieldsformatRange(a,b)collapses shared partshard-code ' - 'wrong dash per localeformatRangeToPartsstyled output

Frequently Asked Questions

How do I format a date range in JavaScript?

Build an Intl.DateTimeFormat and call formatRange(startDate, endDate). It collapses fields the two dates share — printing 'Mar 3 - 5, 2024' instead of repeating the month and year — and uses the correct range separator for the locale.

How do I customize the markup of a formatted range?

Use formatRangeToParts(start, end). It returns an array of tokens, each tagged with a source of startRange, endRange, or shared, so you can wrap the start and end portions in your own elements while keeping locale-correct text.