Explain Codes LogoExplain Codes Logo

How to format time since xxx e.g. “4 minutes ago” similar to Stack Exchange sites

javascript
time-formatting
date-handling
performance
Anton ShumikhinbyAnton Shumikhin·Oct 15, 2024
TLDR

Get immediate "time ago" results using JavaScript's Date API with this nifty timeSince function:

function timeSince(date) { // List of time periods (in seconds) and their names const periods = [ { time: 31536000, name: 'year' }, { time: 2592000, name: 'month' }, { time: 86400, name: 'day' }, { time: 3600, name: 'hour' }, { time: 60, name: 'minute' }, { time: 1, name: 'second' } ]; // Magic time conversion trick, don't tell your friends! let elapsed = Math.floor((Date.now() - date) / 1000); for (let { time, name } of periods) { if (elapsed >= time) { let count = Math.floor(elapsed / time); // Adding 's' for plural, 'cause you ain't reading "1 minutes ago" return `${count} ${name}${count > 1 ? 's' : ''} ago`; } } } // Example usage: console.log(timeSince(new Date(Date.now() - 150 * 1000))); // "2 minutes ago"

This code calculates the elapsed time by subtracting Date.now() and divides it by 1000 to convert milliseconds to seconds. It then loops over the various periods and returns the largest frame that fits the elapsed time.

Using libraries for better time formatting

Sometimes, we need heavier artillery for tackling time:

  • Moment.js: You can handle the entire universe's time with yourdate.fromNow(). However, you might want to look at alternatives as it is now a legacy project.
  • Luxon and Day.js: These are modern knights fighting the same battles as Moment.js with similar weapons but different styles. Both are modern and have their fans.
  • Intl.RelativeTimeFormat: If your application speaks multiple languages, you should hit up Intl.RelativeTimeFormat. It does language-sensitive relative time formatting and can even handle future dates (yes, it's a time machine! 😉).

Formatting time in TypeScript

Considering static typing for robust performance? Let's throw TypeScript into the mixture. It's JavaScript that scales!💪

interface TimePeriod { time: number; name: string; } function timeSince(date: Date): string { const periods: TimePeriod[] = [ { time: 31536000, name: 'year' }, { time: 2592000, name: 'month' }, { time: 86400, name: 'day' }, { time: 3600, name: 'hour' }, { time: 60, name: 'minute' }, { time: 1, name: 'second' } ]; let elapsed: number = Math.floor((Date.now() - date.getTime()) / 1000); for (let { time, name } of periods) { if (elapsed >= time) { let count: number = Math.floor(elapsed / time); return `${count} ${name}${count > 1 ? 's' : ''} ago`; } } return "just now"; // Time traveler found 🚀 }

JavaScript special time cases

Handle the unexpected:

  • Just a moment ago: Use this message for events that just occurred.
  • In the future: Rare in "time ago" context but let's cover it anyway. Use Math.abs() to tackle those negative values for elapsed time.
  • Time complexities: Don't forget the nuances when calculating months and years. February hates being the shortest month. 😅

Updating Times: The Live Show

For enthusiast live display fans, keep these two points in mind:

  • Live updates: Consider setting timed refreshes or use WebSockets for updating timestamps without a user reloading the page.
  • Memory and speed: Handle many timestamps with caution, multiple live updates can throw a performance party in the memory space.

Some best practices to remember when dealing with dates in JavaScript:

  • ISO 8601 timestamps: Conform for interoperability and avoid split headaches, especially when using libraries like jquery.timeago.
  • Embrace immutability: Enjoy drama-free date handling, use libraries promoting immutability (like Luxon and Day.js).
  • Comparing Date objects: Be careful when there are time zones and daylight saving time changes involved. It's like mixing cats and dogs, you never know what can happen!