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
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:
~
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 or follow @bramusblog on Twitter.
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.