Relative Date Formatting in JavaScript

Did you know those behemothic JavaScript date libraries are basically obsolete?

A few weeks ago I learnt how to use the Internationalization API for natural alphanumeric sorting. This week I’ve been using the DateTimeFormat and RelativeTimeFormat APIs. Like all native APIs they’re a little verbose and unintuitive, but also extremely powerful. Browser support has caught up making them viable for regular use.

A simple example for the day of the week:

// Returns "Sunday" (on Sunday)
new Date().toLocaleDateString(
  'en-GB', {weekday: 'long'}
);

The same using a class instance:

const dtf = new Intl.DateTimeFormat(
  'en-GB', {weekday: 'long'}
);
dtf.format(new Date());

An example using relative formats:

const rtf = new Intl.RelativeTimeFormat(
  'en-GB', {numeric: 'auto'}
);
rtf.format(-1, 'days'); // Returns "yesterday"
rtf.format(-2, 'days'); // Returns "2 days ago"

The Intl namespace is chock full of goodies. Best of all browsers provide full internationalisation so translating web UI becomes even easier.

Practical Example

For my podcast player side project I’ve added a function to format dates based on age. Most recent episode are labelled either “Today” or “Yesterday”, or by weekday if less than seven days olds, and finally by full date when older.

podcast episode list with relative dates

This makes it easier to see what’s new at a glance. It removes the need to mentally parse a full date. For older episodes a relative format like “20 days ago” is less meaningful so I’ve opted for long form after one week. Context is key, there’s no magic formula.

// Formatter for "Today" and "Yesterday" etc
const relative = new Intl.RelativeTimeFormat(
  'en-GB', {numeric: 'auto'}
);

// Formatter for weekdays, e.g. "Monday"
const short = new Intl.DateTimeFormat(
  'en-GB', {weekday: 'long'}
);

// Formatter for dates, e.g. "Mon, 31 May 2021"
const long = new Intl.DateTimeFormat(
  'en-GB', {
  weekday: 'short',
  day: 'numeric',
  month: 'short',
  year: 'numeric'
});

const formatDate = (date) => {
  const now = new Date().setHours(0, 0, 0, 0);
  const then = date.setHours(0, 0, 0, 0);
  const days = (then - now) / 86400000;
  if (days > -6) {
    if (days > -2) {
      return relative.format(days, 'day');
    }
    return short.format(date);
  }
  return long.format(date);
};

For me this is more of a lesson in paying attention to what’s being shipped in modern browsers. I’m accustomed to using particular libraries because JavaScript was pretty much stagnant for the longest time. With Internet Explorer 11 no longer a concern, new APIs are becoming usable at an exciting pace.