javascript 33 lines · 6 steps

Building a human-friendly timeAgo formatter

A relative-time helper walks a table of unit divisors to pick the largest fitting unit and format it with Intl.

Explained by highlit
1const DIVISIONS = [
2 { amount: 60, unit: 'seconds' },
3 { amount: 60, unit: 'minutes' },
4 { amount: 24, unit: 'hours' },
5 { amount: 7, unit: 'days' },
6 { amount: 4.34524, unit: 'weeks' },
7 { amount: 12, unit: 'months' },
8 { amount: Number.POSITIVE_INFINITY, unit: 'years' },
9];
10 
11export function timeAgo(input, locale = 'en') {
12 const date = input instanceof Date ? input : new Date(input);
13 
14 if (Number.isNaN(date.getTime())) {
15 throw new TypeError('timeAgo expects a valid date');
16 }
17 
18 const formatter = new Intl.RelativeTimeFormat(locale, {
19 numeric: 'auto',
20 style: 'long',
21 });
22 
23 let duration = (date.getTime() - Date.now()) / 1000;
24 
25 for (const { amount, unit } of DIVISIONS) {
26 if (Math.abs(duration) < amount) {
27 return formatter.format(Math.round(duration), unit);
28 }
29 duration /= amount;
30 }
31 
32 return formatter.format(Math.round(duration), 'years');
33}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A ladder of divisors lets you promote a raw second count into the largest natural unit without a chain of if-statements.
  2. 2Intl.RelativeTimeFormat handles pluralization, sign, and words like 'yesterday' so you never hand-build strings.
  3. 3Keeping the sign on duration means both past and future dates format correctly from the same loop.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a human-friendly timeAgo formatter — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code