Relative Time Formatting in JavaScript’s Intl Library: A Comprehensive Guide

JavaScript
5 minutes read

Introduction

Hello developers! Today, we will discuss an essential concept in JavaScript – Relative Time Formatting, using the Intl library. By understanding and mastering this, you can make your applications more user-friendly, presenting time data more intuitive.

What is Relative Time Formatting?

Relative Time Formatting is a way to express time or dates relative to the current moment, or “now”. Think of phrases like “3 hours ago“, “2 months ago“, or “in 5 days“. These representations are more meaningful and easier to understand for users.

The Intl library is a built-in JavaScript library, designed for language-sensitive string comparison, number formatting, and, you guessed it, date and time formatting. One of its constructors is the RelativeTimeFormat object, which we’re going to explore today.

Basic Usage of Intl.RelativeTimeFormat

Using Intl.RelativeTimeFormat is straightforward. Here is how it works

  • The format method formats a value and a unit into the correctly formatted string, respecting the current locale and formatting options. 
  • The value should be a number (either positive or negative), 
  • The unit a string that represents a valid unit of time like year, quarter, month, week, day, hour, minute, or second.

Here are some basic examples in JavaScript:

Example 1: Formatting various time units in English

let rtf = new Intl.RelativeTimeFormat('en');

console.log(rtf.format(-1, 'year')); // Output: '1 year ago'
console.log(rtf.format(1, 'quarter')); // Output: 'in 1 quarter'
console.log(rtf.format(0, 'month')); // Output: 'in 0 months'
console.log(rtf.format(-1, 'week')); // Output: '1 week ago'
console.log(rtf.format(1, 'day')); // Output: 'in 1 day'
console.log(rtf.format(0, 'hour')); // Output: 'in 0 hours'
console.log(rtf.format(-1, 'minute')); // Output: '1 minute ago'
console.log(rtf.format(1, 'second')); // Output: 'in 1 second'

In this example, you can see how different units of time are formatted in English. This usage covers both past (using negative numbers) and future (using positive numbers) time.

Example 2: Using auto numeric option

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

console.log(rtf.format(-1, 'year')); // Output: 'last year'
console.log(rtf.format(1, 'year')); // Output: 'next year'
console.log(rtf.format(0, 'day')); // Output: 'today'
console.log(rtf.format(-1, 'day')); // Output: 'yesterday'
console.log(rtf.format(1, 'day')); // Output: 'tomorrow'

Here, the numeric option is set to ‘auto’, which allows the method to output non-numeric phrases like yesterday, today, and tomorrow.

Example 3: Using other locales

let rtf = new Intl.RelativeTimeFormat('fr');

console.log(rtf.format(-1, 'day')); // Output: 'il y a 1 jour'
console.log(rtf.format(1, 'day')); // Output: 'dans 1 jour'
console.log(rtf.format(-1, 'week')); // Output: 'il y a 1 semaine'
console.log(rtf.format(1, 'week')); // Output: 'dans 1 semaine'

In this example, we’re using French (fr) as the locale. This demonstrates how Intl.RelativeTimeFormat can be used with different languages.

Advanced Options in Intl.RelativeTimeFormat

Intl.RelativeTimeFormat offers several options that allow you to customize the output:

  • localeMatcher: The locale matching algorithm to use. Possible values are lookup and best fit. The default is best fit.
  • numeric: The format of the output message. Possible values are always (always use numeric form) and auto (allows output like “yesterday”). The default is always.
  • style: The length of the internationalized message. Possible values are: long (default), short, or narrow.

Here are more examples showcasing the advanced options in Intl.RelativeTimeFormat:

Example 1: Different style options

Here’s how you can use the style option:

let rtf_long = new Intl.RelativeTimeFormat('en', { style: 'long' });
console.log(rtf_long.format(-1, 'day')); // Output: '1 day ago'

let rtf_short = new Intl.RelativeTimeFormat('en', { style: 'short' });
console.log(rtf_short.format(-1, 'day')); // Output: '1 day ago'

let rtf_narrow = new Intl.RelativeTimeFormat('en', { style: 'narrow' });
console.log(rtf_narrow.format(-1, 'day')); // Output: '1 day ago'

In this example, we’re comparing the outputs for ‘long’, ‘short’, and ‘narrow’ styles. However, for English, you won’t see a difference when formatting days. The differences appear more for larger time units as shown below:

console.log(rtf_long.format(-1, 'quarter')); // Output: '1 quarter ago'
console.log(rtf_short.format(-1, 'quarter')); // Output: '1 qtr. ago'
console.log(rtf_narrow.format(-1, 'quarter')); // Output: '1 qtr. ago'

Example 2: Using auto numeric option with narrow style

Here’s how you can use both ‘numeric’ and ‘style’ options together:

let rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto', style: 'narrow' });

console.log(rtf.format(-1, 'year')); // Output: 'last yr.'
console.log(rtf.format(1, 'year')); // Output: 'next yr.'
console.log(rtf.format(0, 'day')); // Output: 'today'
console.log(rtf.format(-1, 'day')); // Output: 'yesterday'
console.log(rtf.format(1, 'day')); // Output: 'tomorrow'

In this example, the style is set to ‘narrow’, which gives shorter output. The numeric is set to auto which allows for outputs like yesterday or tomorrow, but also shortens other units like year to yr.

Example 3: Different localeMatcher options

let rtf_lookup = new Intl.RelativeTimeFormat('en-GB', { localeMatcher: 'lookup' });
console.log(rtf_lookup.format(-1, 'day')); // Output: '1 day ago'

let rtf_bestFit = new Intl.RelativeTimeFormat('en-GB', { localeMatcher: 'best fit' });
console.log(rtf_bestFit.format(-1, 'day')); // Output: '1 day ago'

In this example, the two options for localeMatcher (lookup and best fit) give the same result, because en-GB is directly supported. However, if a certain locale isn’t directly supported, these two options can give different results. The best fit algorithm will find the best match among the available locales, while lookup will find an exact match or default to the runtime’s default locale.

Intl.RelativeTimeFormat Properties and Methods

Intl.RelativeTimeFormat has the following properties and methods that allow you to access more advanced features:

Intl.RelativeTimeFormat.prototype

This property allows you to add properties to the Intl.RelativeTimeFormat object. This can be useful if you want to define custom methods or properties that you want to be accessible to all instances of Intl.RelativeTimeFormat.

Intl.RelativeTimeFormat.prototype.customMethod = function () {
  console.log('This is a custom method!');
};

let rtf = new Intl.RelativeTimeFormat('en');
rtf.customMethod(); // Output: 'This is a custom method!'

In this example, we add a custom method to the Intl.RelativeTimeFormat.prototype and we can then call this method on any Intl.RelativeTimeFormat object.

Intl.RelativeTimeFormat.supportedLocalesOf()

This method returns an array containing the locales that are supported without having to fall back to the default locale of the JavaScript environment. This can help determine which locales you can provide to your users.

let locales = ['ban', 'id-u-nu-latn', 'de-ID'];
let options = {numeric: 'auto'};
let supported = Intl.RelativeTimeFormat.supportedLocalesOf(locales, options);

console.log(supported); // Output: ['ban', 'id-u-nu-latn', 'de-ID']

In this example, we check which of the provided locales are supported. As ban, id-u-nu-latn, and de-ID are all supported, they’re all returned in the resulting array.

Intl.RelativeTimeFormat.prototype.format(value, unit)

This method allows you to format a number and a time unit into a correctly formatted string according to the current locale and formatting options. This is the primary method you’ll be using.

let rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
console.log(rtf.format(-1, 'day')); // Output: 'yesterday'

In this example, we’re formatting the number -1 and the unit day into the string yesterday.

Intl.RelativeTimeFormat.prototype.formatToParts(value, unit)

This method is similar to format(value, unit), but instead of returning a string, it returns an array of objects, where each object represents a part of the formatted string. This can be useful for more advanced formatting needs.

let rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
console.log(rtf.formatToParts(-1, 'day')); 

// Output: 
// [
//   {type: "literal", value: "in "},
//   {type: "integer", value: "1", unit: "day"},
//   {type: "literal", value: " "}
// ]

In this example, we’re splitting the formatting of the number -1 and the unit day into its parts.

Understanding Compatibility and Standards

It’s noteworthy that Intl.RelativeTimeFormat is part of ECMAScript Internationalization API specification (ECMA-402). This standard, maintained by the ECMA International standards organization, ensures consistency across different JavaScript environments. While most modern JavaScript environments implement this standard, there might be differences or missing features in older or less common environments. Testing your code in your target environments is always a good idea!

Further Reading

Conclusion

In conclusion, the Intl.RelativeTimeFormat function within the JavaScript Internationalization API is a powerful and versatile tool that simplifies the process of formatting relative times based on the locale. Its use can greatly enhance the localization experience for users worldwide by providing them with date and time information in a format that’s familiar to them. Happy coding!

Leave a Reply

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