HomeCategoriesAll Tags

Native Intl api now provides relative time strings

Want to convert date time into relative time strings like yesterday, tomorrow, 1 day ago, 1 week ago, then modern browser's native Intl is enough. Let's see how to accomplish it...

No need to use moment.js or date-fns or any such library. Here, we will make use of browser's native Intl api.

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

rtf.format(1, 'day') // 'tomorrow'
rtf.format(-3, 'year') // '3 years ago'
rtf.format(15, 'minute') // 'in 15 minutes'

Thus bypassing numeric value and time unit we can get the relative time string in the locale of our choice.

Instead of en as language we can also pass navigator.language to get the language from user's browser and present the result in that.

In the second argument supported units include: "year", "quarter", "month", "week", "day", "hour", "minute", and "second". So, here is a small function that solves this problem of what unit to pass for a given date.

/**
 * Convert a date to a relative time string, such as
 * "a minute ago", "in 2 hours", "yesterday", "3 months ago", etc.
 * using Intl.RelativeTimeFormat
 */
export function getRelativeTimeString(date: Date | number, lang = navigator.language): string {
  // Allow dates or times to be passed
  const timeMs = typeof date === 'number' ? date : date.getTime()

  // Get the amount of seconds between the given date and now
  const deltaSeconds = Math.round((timeMs - Date.now()) / 1000)

  // Array reprsenting one minute, hour, day, week, month, etc in seconds
  const cutoffs = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity]

  // Array equivalent to the above but in the string representation of the units
  const units: Intl.RelativeTimeFormatUnit[] = ['second', 'minute', 'hour', 'day', 'week', 'month', 'year']

  // Grab the ideal cutoff unit
  const unitIndex = cutoffs.findIndex((cutoff) => cutoff > Math.abs(deltaSeconds))

  // Get the divisor to divide from the seconds. E.g. if our unit is "day" our divisor
  // is one day in seconds, so we can divide our seconds by this to get the # of days
  const divisor = unitIndex ? cutoffs[unitIndex - 1] : 1

  // Intl.RelativeTimeFormat do its magic
  const rtf = new Intl.RelativeTimeFormat(lang, { numeric: 'auto' })
  return rtf.format(Math.floor(deltaSeconds / divisor), units[unitIndex])
}

Just pass the date in the given function and it will give the relative time from the current time.

Some more useful things Intl can do for us

  1. Intl.DateTimeformat gives basic date and time formatting capability.
  2. Intl.NumberFormat is helpful in formatting number and currency.

Another good thing is that it is supported in Node.js as well so not just in browser but it can be used on server side as well.

Hope this was helpful. Thanks!!

- Ayush 🙂