
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 (i.e. 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
languagepart is a 2/3 character long ISO 639-1 code - The (optional)
scriptpart is a 4 character long ISO 15924 code - The (optional)
regionpart 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:
-
localeMatcherThe locale matching algorithm to use. Possible values are
"lookup"and"best fit"(default) -
numericThe 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") -
styleThe 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:
~
Thank me with a coffee.
I don’t do this for profit but a small one-time donation would surely put a smile on my face. Thanks!
To stay in the loop you can follow bramus on Bluesky or subscribe to the RSS feed.
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?
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.