Show Yesterday, Today, and Tomorrow Labels in JavaScript

To show word labels like 'yesterday' and 'tomorrow', use Intl.RelativeTimeFormat(locale, { numeric: 'auto' }) and pass the day difference computed in the user's zone. Part of Intl.RelativeTimeFormat for Relative Dates.

Why this scenario is tricky

Two things make this deceptively easy to break: the day difference must be counted in calendar days in the user's zone, not by dividing a millisecond gap (which flips at the wrong moment), and the friendly words only appear when you set numeric: 'auto' — the default 'always' prints '1 day ago' instead of 'yesterday'.

'auto' unlocks the wordsDefault numeric prints '1 day ago''auto' unlocks the wordsnumeric default ('always')'in 1 day', '1 day ago'numeric: 'auto''tomorrow', 'yesterday'Set numeric to 'auto' and count whole calendar days in the user's zone.

Minimal working solution

import { Temporal } from '@js-temporal/polyfill';
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
const days = today.until(target, { largestUnit: 'day' }).days; // both PlainDate in user's zone
rtf.format(days, 'day'); // -1 -> 'yesterday', 0 -> 'today', 1 -> 'tomorrow'

Day diff to wordsSigned day count, numeric autoDay diff to wordstoday, targetuntil .daysrtf.format(days,'day')

Full production version

import { Temporal } from '@js-temporal/polyfill';
function dayLabel(target: Temporal.PlainDate, zone: string, locale = 'en'): string {
  const today = Temporal.Now.plainDateISO(zone);
  const days = today.until(target, { largestUnit: 'day' }).days;
  return new Intl.RelativeTimeFormat(locale, { numeric: 'auto' }).format(days, 'day');
}

Zoned label helperCompute today in the zone, then formatZoned label helpertarget,zonetoday in zoneuntil .dayslabel

Verification snippet

Yesterday/Today/Tomorrow assertionsKey cases assert correctlyAssertions that prove the edge casedays == -1'yesterday'days == 0'today'days == 1'tomorrow'days == -3'3 days ago'

Common pitfalls

Yesterday/Today/Tomorrow pitfallsCommon mistakes and their fixesWrongRightdivide ms by 86.4Mflips at wrong instantuntil(...).days in zonecalendar-correctnumeric default'1 day ago' not 'yesterday'numeric: 'auto'friendly words

Frequently Asked Questions

How do I show 'yesterday' and 'tomorrow' instead of '1 day ago'?

Create Intl.RelativeTimeFormat with numeric: 'auto' and pass the signed day difference. With 'auto', values of -1, 0, and 1 render as 'yesterday', 'today', and 'tomorrow'; the default 'always' would print '1 day ago' and 'in 1 day'.

How do I compute the day difference correctly?

Count whole calendar days in the user's time zone, for example with Temporal.PlainDate.until(target, { largestUnit: 'day' }).days where both dates are PlainDates in that zone. Dividing a millisecond gap by 86,400,000 flips at the wrong moment near midnight.