Easy human-readable date difference

This article presents a very simple method to show messages like “20 seconds ago” or “3 weeks ago” or any other arbitrary date difference expressed in human-readable form in Javascript without relying on libraries like moment.js. The method applies to other programming languages as well, but may need adjustments depending on their quirks.

Fundamentally all languages store dates as the number of (milli)seconds since January 1st, 1970, UTC (GMT). This is called the Unix time(stamp). Javascript stores the number as milliseconds, but other languages (such as SQL) use seconds. Dates prior to 01-01-1970 are stored as negative numbers. This arrangement makes it easy to calculate date differences, but there are a few gotcha’s.

Javascript (and Java) interprets 2-digit years as years after 1900. So constructing a date with new Date(10, 0, 1) will be interpreted as January 1st, 1910. If you need to handle dates prior to 1900, you have to write some extra code – first you need to construct date date, e.g. new Date(1200, 0, 1) and then set the year you want with setYear(10).

The other thing you need to think about is time zones. Either work with UTC or the local timezone but don’t mix them.

Getting the difference between two dates is very easy:

let date1 = new Date(2022, 02, 30);
let date2 = new Date(1982, 10, 15);
let diff = Math.abs(date2.getTime() - date1.getTime()); //1242428400000

This works, but it’s not terribly helpful.

What we need to do now is to convert the milliseconds into seconds, days or years. To do this we need to compare the millisecond difference with different time intervals and find the best fit.

Assume we have a time difference of 80,000,000 milliseconds, or 80,000 seconds. There are 86,400 seconds in a day, so our timespan is less than a day and it wouldn’t make sense to write “0.92 days”. The next interval would be hours. There are 3,600 seconds in an hour, so 80,000 seconds would mean 22 hours.

This is how our algorithm will work.

This article will use English only as localization would be outside the scope of this exercise. Keep in mind that different languages form plurals in different ways, so if you need multiple languages, you’ll have to take that into account.

/**
 * Calculate the difference between two dates.
 * @param {Date} date1 first date
 * @param {Date} date2 second date
 * @return {[number, string]} array containing the difference and the time unit of measure
 */
function timeDiff(date1, date2) {
	if (!(date1 instanceof Date && date2 instanceof Date))
		throw new RangeError('Invalid date arguments');
		
	const timeIntervals = [31536000, 2628000, 604800, 86400, 3600, 60, 1];
	const intervalNames = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second'];

	const diff = Math.abs(date2.getTime()-date1.getTime())/1000;
	const index = timeIntervals.findIndex(i=>(diff/i) >= 1);
	const n = Math.floor(diff/timeIntervals[index]);
	const interval = intervalNames[index];

	return [n, interval];
}

/**
 * Format a date difference into a string.
 * @param {number} value numeric value
 * @param {string} str time unit
 * @return {string} value and unit, taking plurals into account
 */
function localize(value, str)
{
	if (value != 1)
		str += 's';
		
	return `${value} ${str} ago`
}

localize(...timeDiff(new Date(2022, 0, 1), new Date(2021, 0, 1)));
// 1 year ago

localize(...timeDiff(new Date(2022, 0, 1), new Date(2010, 0, 1)));
// 12 years ago

localize(...timeDiff(new Date(2022, 2, 20), new Date(2022, 0, 1)));
// 2 months ago

localize(...timeDiff(new Date(2022, 2, 30, 14, 10, 5), new Date(2022, 2, 30, 13, 9, 0)));
// 1 hour ago
Picture of Armand Niculescu

Armand Niculescu

Senior Full-stack developer and graphic designer with over 25 years of experience, Armand took on many challenges, from coding to project management and marketing.

One Response

Comments are closed.