ESNext: Relatively format time with Intl.RelativeTimeFormat

A proposal to the ECMAScript Internationalization API Specification (ECMA-402) that just moved to Stage-4, is Intl.RelativeTimeFormat. It’s a way to format time in a relative manner – e.g. “10 minutes ago” or “in 3 quarters” – while also taking a language into account.

🤔 Note that this proposal is part of ECMA-402 (the ECMAScript Internationalization API Specification) and not ECMA-262 (The ECMAScript Language Specification). Therefore this proposal will not be part of the yearly "ES20XX”, as that term only applies to ECMA-262.

To advance proposals in ECMA-402, TC39 uses the same 5-stage-process as they use in ECMA-262.

💁‍♂️ Stage-4?

The Technical Committee which is concerned with the standardization of ECMAScript (e.g. TC39) has a 5 stage process in place, ranging from stage-0 to stage-4, by which it develops a new language feature.

Stage-4 is the Finished Stage and indicates that the proposal is ready to become part of the ECMAScript Specification.

const rtf_en = new Intl.RelativeTimeFormat('en');

console.log(rtf_en.format(-1, 'day')); // ~> 1 day ago
console.log(rtf_en.format(30, 'seconds')); // ~> in 30 seconds
const rtf_nl = new Intl.RelativeTimeFormat('nl-BE', {
    localeMatcher: 'best fit', // other values: 'lookup'
    numeric: 'auto', // other values: 'always'
    style: 'short', // other values: 'long' or 'narrow'
});

console.log(rtf_nl.format(-1, 'day')); // ~> gisteren
console.log(rtf_nl.format(30, 'seconds')); // ~> over 30 sec.

~

Syntax

The constructor takes two arguments: locales and options. Both are optional:

new Intl.RelativeTimeFormat([locales[, options]]);

Possible units to use while formatting are "year", "quarter", "month", "week", "day", "hour", "minute", and "second".

~

Constructor Arguments

locales

The locales argument is a BCP 47 language tag which identifies the locale to use. The main parts, amongst others, of a BCP 47 language tag are language, script, and region

  • The language part is a 2/3 character long ISO 639-1 code
  • The (optional) script part is a 4 character long ISO 15924 code
  • The (optional) region part is a 2 alpha or 3 digit long ISO 3166-1 code

When more than one part is present, they are separated by a dash (-)

Examples:

  • "nl": Dutch (language).
  • "nl-BE": Dutch as used in Belgium (language + region).
  • "zh-Hans-CN": Chinese written in simplified characters as used in China (language + script + region).

When locale is omitted, it will default to the language-setting of the browser/system.

options

The options object allows you to tweak how Intl.RelativeTimeFormat behaves. It has the following properties:

  • localeMatcher

    The locale matching algorithm to use. Possible values are "lookup" and "best fit" (default)

  • numeric

    The format of output message. Possible values are "always" (always include a number in the output, default) or "auto" (which allows output such as “in 1 day” to be rewritten in the more natural "tomorrow")

  • style

    The length of the output message, e.g. "in 1 month" vs. "in 1 mo.". Possible values are "long" (default), "short", or “narrow” (which mostly yields the same result as "short", depending on the language)

~

Extracting all parts with formatToParts

If you don’t want a string to be returned, you can use formatToParts() to get an object describing all parts:

const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });

// Format relative time using the day unit.
rtf.formatToParts(-1, "day");
// > [{ type: "literal", value: "yesterday"}]

rtf.formatToParts(100, "day");
// > [{ type: "literal", value: "in " }, { type: "integer", value: "100", unit: "day" }, { type: "literal", value: " days" }]

~

JS/Browser Support

Intl.RelativeTimeFormat shipped in V8 v7.1.179 (included Chrome 71) and Firefox 65.

Polyfills are available:

~

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would always put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

Published by Bramus!

Bramus is a Freelance Web Developer from Belgium. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …)

Join the Conversation

4 Comments

  1. Thanks for this post. I read it last year and kept postponing to try this out. Today I did try it in the browser and it worked as explained. However, when I decided to add it to my project, where I list some entities with a created at timestamp I was wondering how to make it work. So I quickly added dayjs module to my project and found it has a relativeTime plugin.
    With that in hand, what would be the use case for this newety?

    1. Having a native feature for this will save you some extra bytes sent over the wire, and will be faster to execute. This because you don’t need to send/load the extra library/plugin.

      Use cases remain the same.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.