AniUI Academy

Format a timestamp as relative time

mediumDOM & Utilities+250 XPPractice

Every feed, comment thread and notification list shows relative times. Passing now in explicitly rather than reading the clock inside is what makes the function testable — and it is why this version is deterministic.

Implement formatRelativeTime(timestamp, now), both epoch milliseconds. Let seconds = Math.round((now - timestamp) / 1000) — positive means the past — and abs = Math.abs(seconds).

If abs is under 60, return exactly "just now". Otherwise pick the first unit whose limit abs falls under, and take Math.floor(abs / size) as the value: minute (size 60, limit 3600), hour (3600, 86400), day (86400, 604800), week (604800, 2592000), month (2592000, 31536000), and otherwise year (size 31536000).

Build the label as the value, a space, the unit name, plus an s unless the value is exactly 1. Return "<label> ago" when seconds is positive and "in <label>" when it is negative.

Arguments

timestampnumber
The event's epoch milliseconds.
nownumber
The reference time in epoch milliseconds. Passed in rather than read from the clock so the function stays deterministic.

Returns

string — A human relative time: "just now", "<n> <unit> ago" for past times, or "in <n> <unit>" for future times.

Examples

formatRelativeTime(0, 5 * 60 * 1000);
// => "5 minutes ago"

The unit is pluralised when the value is not exactly 1.

formatRelativeTime(0, 3600 * 1000);
// => "1 hour ago"

Exactly one of a unit drops the trailing s.

formatRelativeTime(2 * 86400 * 1000, 0);
// => "in 2 days"

Future times — when now < timestamp — use an in ... prefix instead of a suffix.

What it has to do

  • Return "just now" when the difference is under 60 seconds in either direction.
  • Use the largest unit that fits, flooring the value, across minute, hour, day, week, month and year.
  • Pluralise the unit with a trailing s unless the value is exactly 1.
  • Suffix past times with " ago" and prefix future times with "in ".
  • Take now as an argument rather than reading the clock.

Notes

  • Passing now as an argument is the single trick that makes this function testable: a caller can anchor time at a known value and assert the label without flaky clocks.
  • This uses fixed-size months and years — 30 and 365 days. Real Intl.RelativeTimeFormat output chooses units with calendar awareness, so switch to that when you need localisation and leap-aware boundaries.

Stuck? The javascript course covers everything this challenge needs.

Try it yourself
Loading playground...
5 tests · +250 XP

Discussion

Sign in to post
Sign in to join the conversation.Sign in

No comments yet. Be the first to say something.