Format "5 days ago" localized relative date strings in a few lines with native JavaScript

The native Intl.RelativeTimeFormat API can generate nicely formatted relative date/time strings without any external dependencies.

The internationalization API in browsers has improved a lot in the last few years. This is the first blog post in a series that will show neat little examples that will help you get familiar with the native Intl APIs.

This post will bring up the basics of using the Intl.RelativeTimeFormat constructor for format dates into strings in the popular "5 days ago" used in various services over the internet.

Calculate the delta days

First, we need to calculate how many days have passed since the date, or how many days are left until the date.

This can be done by just subtracting the timestamp of the given date from the timestamp of now.

const date = new Date('2021-02-10');

// Subtract the date with the current, divide to get number of days
const deltaDays = (date.getTime() - Date.now()) / (1000 * 3600 * 24);

(the result would be in milliseconds, to transform into days we need to divide by milliseconds per second, seconds per hour, hours per day, (1000 * 3600 * 24))

Format into a relative date string

Now we can use an Intl.RelativeTimeFormat instance to format our variable deltaDays. As we only want whole days in the result we need to round off deltaDays before formatting.

// Create relative time formatter, if a locale string argument (e.g. 'sv-SE') is
// supplied that locale will be used instead of the browsers selected language
const formatter = new Intl.RelativeTimeFormat();

// Format days to string (rember to round off deltaDays)
const result = formatter.format(Math.round(deltaDays), 'days');

console.log(result);
// Output: 17 days ago

One of the neat features of Intl.RelativeTimeFormat is that it will default to the language the browser runs in and automatically translate the words to that language. If you rather want to lock the translation into a language that the rest of your service uses, just pass that locale as the first argument the the Intl.RelativeTimeFormat constructor like so:

const formatter = new Intl.RelativeTimeFormat('en-US');

When running formatter.format we must also pass in that we want to format our Number into days rather than minutes, hours, and so on (e.g. formatter.format(10, 'days')).

Create a reusable function

Let's rewrite this into a reusable function that will also have an optional locale argument so that it's possible to lock the formatting into a specific locale.

Another small trick here is that the Date constructor can be wrapped into another Date constructor (new Date(new Date())), because of this we can add this wrapping to the first argument to allow the function to accept millisecond timestamps, ISO 8601 strings as well as Date instances.

function formatDaysAgo(value, locale) {
  const date = new Date(value);
  const deltaDays = (date.getTime() - Date.now()) / (1000 * 3600 * 24);
  const formatter = new Intl.RelativeTimeFormat(locale);
  return formatter.format(Math.round(deltaDays), 'days');
}

Example usage:

console.log(formatDaysAgo(new Date('2021-02-10')));
// 17 days ago

console.log(formatDaysAgo(Date.now()));
// in 0 days

console.log(formatDaysAgo('2021-03-13'));
// in 14 days

console.log(formatDaysAgo('2021-03-13', 'sv-SE'));
// om 14 dagar

More information

The Intl.RelativeTimeFormat API provides several more configuration options, you can read more about them at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat .

Happy coding!