Categories
JavaScript

Formatting Relative Time with JavaScript’s RelativeTimeFormat Constructor

Spread the love

With the Intl.RelativeTimeFormat constructor, we can format relative time in a locale sensitive manner with ease. We can style in different ways and format it the way we want. This lets us format relative time strings without much hassle that comes from manipulating strings. The constructor takes 2 arguments. The first argument is for a locale string or an array of such strings. The second is an object which takes variety of arguments for adjusting the relative time string to the way we want. The instance of this constructor has a few methods to return the formatted string, the formatted string as an array of substrings, and a method to return the options that we set for formatting the string.

The first argument for the Intl.RelativeTimeFormat constructor, is the locale, which should be a BCP 47 language tag, or an array of such locale strings. This is an optional argument. It takes a BCP 47 language tag for the locale. An abridged list of BCP 47 language tags include:

  • ar — Arabic
  • bg — Bulgarian
  • ca — Catalan
  • zh-Hans — Chinese, Han (Simplified variant)
  • cs — Czech
  • da — Danish
  • de — German
  • el — Modern Greek (1453 and later)
  • en — English
  • es — Spanish
  • fi — Finnish
  • fr — French
  • he — Hebrew
  • hu — Hungarian
  • is — Icelandic
  • it — Italian
  • ja — Japanese
  • ko — Korean
  • nl — Dutch
  • no — Norwegian
  • pl — Polish
  • pt — Portuguese
  • rm — Romansh
  • ro — Romanian
  • ru — Russian
  • hr — Croatian
  • sk — Slovak
  • sq — Albanian
  • sv — Swedish
  • th — Thai
  • tr — Turkish
  • ur — Urdu
  • id — Indonesian
  • uk — Ukrainian
  • be — Belarusian
  • sl — Slovenian
  • et — Estonian
  • lv — Latvian
  • lt — Lithuanian
  • tg — Tajik
  • fa — Persian
  • vi — Vietnamese
  • hy — Armenian
  • az — Azerbaijani
  • eu — Basque
  • hsb — Upper Sorbian
  • mk — Macedonian
  • tn — Tswana
  • xh — Xhosa
  • zu — Zulu
  • af — Afrikaans
  • ka — Georgian
  • fo — Faroese
  • hi — Hindi
  • mt — Maltese
  • se — Northern Sami
  • ga — Irish
  • ms — Malay (macrolanguage)
  • kk — Kazakh
  • ky — Kirghiz
  • sw — Swahili (macrolanguage)
  • tk — Turkmen
  • uz — Uzbek
  • tt — Tatar
  • bn — Bengali
  • pa — Panjabi
  • gu — Gujarati
  • or — Oriya
  • ta — Tamil
  • te — Telugu
  • kn — Kannada
  • ml — Malayalam
  • as — Assamese
  • mr — Marathi
  • sa — Sanskrit
  • mn — Mongolian
  • bo — Tibetan
  • cy — Welsh
  • km — Central Khmer
  • lo — Lao
  • gl — Galician
  • kok — Konkani (macrolanguage)
  • syr — Syriac
  • si — Sinhala
  • iu — Inuktitut
  • am — Amharic
  • tzm — Central Atlas Tamazight
  • ne — Nepali
  • fy — Western Frisian
  • ps — Pushto
  • fil — Filipino
  • dv — Dhivehi
  • ha — Hausa
  • yo — Yoruba
  • quz — Cusco Quechua
  • nso — Pedi
  • ba — Bashkir
  • lb — Luxembourgish
  • kl — Kalaallisut
  • ig — Igbo
  • ii — Sichuan Yi
  • arn — Mapudungun
  • moh — Mohawk
  • br — Breton
  • ug — Uighur
  • mi — Maori
  • oc — Occitan (post 1500)
  • co — Corsican
  • gsw — Swiss German
  • sah — Yakut
  • qut — Guatemala
  • rw — Kinyarwanda
  • wo — Wolof
  • prs — Dari
  • gd — Scottish Gaelic

The second argument accepts an object with a few properties — localeMatcher , numeric , and style .

The localeMatcher option specifies the locale matching algorithm to use. The possible values are lookup and best fit . The lookup algorithm search for the locale until it finds the one that fits the character set of the strings that are being compared. best fit finds the locale that is at least but possibly more suited that the lookup algorithm.

The numeric option lets us set the option for how the formatted string’s message is outputted. The possible values are always which is like ‘2 days ago’, or auto , for example, like ‘yesterday’. The auto allows us to not always use numeric values for output. The style option lets us change the length of the internationalized message. The possible values are long , short , or narrow . long would output something like ‘in 2 months’, short would be something like ‘in 2 mo.’, and narrow would be something like in ‘in 2 mo.’. It could be similar to the short style in some locales.

Instances of the Intl.RelativeTimeFormat constructor has a few methods. It has the format method to get the formatted relative time string with the value and the unit according to the locale and the formatting option that’s given in the constructor. The formatToParts method is similar to the format method except that the formatted string is returned as an array instead of a string. The resolvedOptions method returns an object with the options that we set in the constructor for formatting the string and the locale that were set.

The format method takes 2 arguments. The first is the value for quantity of the relative date and the second is the time unit in string form. For example, we can format relative dates with the format method like in the following code:

const rtf = new Intl.RelativeTimeFormat("en", {
  localeMatcher: "best fit",
  numeric: "always",
  style: "long",
});

console.log(rtf.format(-1, "day"));

The code above would log ‘1 day ago’ since we specified the value of the relative date to be -1, which means 1 day before today, and the time unit is day . We can also put in other units, for example, if we want minutes, then we get:

const rtf = new Intl.RelativeTimeFormat("en", {
  localeMatcher: "best fit",
  numeric: "always",
  style: "long",
});

console.log(rtf.format(-10, "minute"));

Then we get ’10 minutes ago’ from the console.log statement. We can also change the style and the length. For example, we can write:

const rtf = new Intl.RelativeTimeFormat("en", {
  localeMatcher: "best fit",
  numeric: "auto",
  style: "short",
});
console.log(rtf.format(10, "minute"));

Then we get ‘in 10 min.’ from the console.log statement since we have positive 10 instead of negative 10 which is 10 minutes from the current time. Also we had the short style. which abbreviates the unit.

We can also change the locale for different locales. For example, we can write:

const rtf = new Intl.RelativeTimeFormat("zh-hant", {
  localeMatcher: "best fit",
  numeric: "auto",
  style: "long",
});
console.log(rtf.format(1, "minute"));

to get the relative date time string in Chinese Traditional characters instead of English. If we run the console.log statement in the code above, we get ‘1 分鐘後’, which means 1 minute later.

We can get the formatted string in an array of string parts with the formatToParts() method. It returns an array of substrings of the formatted strings. For example, we can call it like in the following code:

const rtf = new Intl.RelativeTimeFormat("en", {
  localeMatcher: "best fit",
  numeric: "always",
  style: "long",
});

const parts = rtf.formatToParts(-1, "day");
console.log(parts);

The code above would get us:

[
  {
    "type": "integer",
    "value": "1",
    "unit": "day"
  },
  {
    "type": "literal",
    "value": " day ago"
  }
]

with the console.log statement in the code above.

The method works equally well with non-English locales. For example, we can write:

const rtf = new Intl.RelativeTimeFormat("zh-hant", {
  localeMatcher: "best fit",
  numeric: "auto",
  style: "long",
});

const parts = rtf.formatToParts(1, "minute")
console.log(parts);

Then we get:

[
  {
    "type": "integer",
    "value": "1",
    "unit": "minute"
  },
  {
    "type": "literal",
    "value": " 分鐘後"
  }
]

with the console.log statement in the code above.

The resolvedOptions() method gets us an object with the options that we set in the constructor for formatting the string and the locale that were set. We can use it like in the following code:

const rtf = new Intl.RelativeTimeFormat("zh-hant", {
  localeMatcher: "best fit",
  numeric: "auto",
  style: "long",
});
console.log(rtf.`resolvedOptions`());

With the code above, we get the following from the console.log statement:

{
  "locale": "zh-Hant",
  "style": "long",
  "numeric": "auto",
  "numberingSystem": "latn"
}

The Intl.RelativeDateFormat constructor also has a supportedLocalesOf method to get us the supported locales for formatting date and time. It take an array of BCP 47 locale strings as an argument. Unicode extension keys will be returned along with the locale code even though it has no relevance for date formatting if provided. It takes the localeMatcher option to specify the locale matching algorithm to use. The possible values are lookup and best fit . The lookup algorithm search for the locale until it finds the one that fits the character set of the strings that are being compared. best fit finds the locale that is at least but possibly more suited that the lookup algorithm.

For example, we can use it like in the following code:

const locales = ['en-ca', 'id-u-co-pinyin', 'ban'];
const options = {
  localeMatcher: 'lookup'
};

console.log(Intl.RelativeTimeFormat.supportedLocalesOf(locales, options));

Then we get [“en-CA”, “id-u-co-pinyin”] . This is because Balinese is similar enough to Indonesian to be considered the same for the lookup algorithm. Note that the Unicode extensions that are in the input array are returned along with the output even though it has no relevance in this context.

The JavaScript Intl.RelativeTimeFormat constructor let us format relative time in a locale sensitive manner with ease. We can style in different ways and format it the way we want. This lets us format relative time strings without much hassle that comes from manipulating strings. The constructor takes 2 arguments. The first argument is for a locale string or an array of such strings. The second is an object which takes variety of arguments for adjusting the relative time string to the way we want. The instance of this constructor has a few methods to return the formatted string, the formatted string as an array of substrings, and a method to return the options that we set for formatting the string. We can also check the locales that supported with the static supportedLocalesOf method.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

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