When trying to make things “user friendly”, a lot of good ideas seem to get subjected to Chinese Whispers or Cargo Cult treatment, and end up either counter-productive, or at least unnecessarily annoying. One such idea is formatting dates and times as easy-to-read relative terms, like “5 minutes ago”, or “yesterday”. It makes everything look less dull and technical, and someone probably did some studies once showing that it’s easier to read in some particular context. On the other hand, it means actively hiding information from the user, and is implemented badly more often than well, leading to sentences that are misleading, useless, or just plain nonsense.

If you are ever implementing such a feature for an application or as a library, below are some tips which you really should consider. If you have written, or come across, a library that does it right, please let me know in the comments, so I can shout at anyone who uses something braindead instead.

Don’t Mix Past and Future

I have lost count of the number of applications and websites which will happily generate sentences like “Rowan posted this in 2 minutes time”, or “your order will be ready 2 minutes ago”. Unless your application also incorporates a flux capacitor or a tachyonic antitelephone, both of these sentences are clearly complete nonsense.

Somehow, the likes of JIRA and Github have failed to fix this obvious bug, perhaps because they’ve focussed on the wrong part of the problem, thinking it would go away if they could ensure all timestamps were from perfectly reliable monotonic time sources. The actual fix is insultingly simple: if you know that all your durations should be in the past, never output future wording (and vice versa). If the difference comes in the wrong side of zero, fall back to a fixed string like “just now”, or just print the full date and time; it really couldn’t be much simpler.

Use a Relevant Range

Checking if a value is more or less than zero is of course just a special case of defining a limit on your expected values; in some cases, that might need to be more complicated than “any time in the past” or “any time in the future”. Windows Explorer has a neat feature where it can group the files in a folder by date, and it will create headings like “Today”, “Yesterday”, and “Earlier this month” – a feature which is rendered completely useless by always using today as the baseline. If you come back to the folder day after day, you’ll see the same files shunted into broader and broader categories, until everything in the folder is more than a month old, at which point they all appear under the childish heading “a long time ago”. It’s as though it didn’t occur to whoever designed the feature that files don’t age away like food in the fridge.

In this case, we can know exactly what the range is – we can easily sort the files and find the oldest and newest, particularly on an indexed file system. Taking that range, we can start by assuming it’s evenly distributed and automatically select “group by day”, “group by week”, or “group by month” (actually, just offering those as manual options would be infinitely better than what Microsoft shipped). If we want to get clever, we can have a target for how many bands we want, and let them have uneven definitions: we might end up with “Jan 2000”, “Feb 2000 – Dec 2001”, “2002 – 2017”, and “This year” as headings in a particular folder. This is the kind of algorithm that you can write in an afternoon, but which looks like magic when tuned well.

Choose a Minimum and Maximum Resolution

If dynamically adjusting bucket sizes isn’t possible or appropriate for a particular use case, some common sense should be applied instead. If you have a list of events all labelled “7 years ago”, how does the user know whether there was a burst of activity at Christmas, or a steady flow through the year? The simplest solution might be to define a “maximum vagueness” of 1 month, and display strings such as “6 years 9 months ago”. Make sure that’s consistent though, so users don’t have to guess if “7 years” means “7 years and less than 1 month” or “between 6.5 and 7.5 years”, like guessing if “the 1900s” represents 10 years or 100.

It’s worth examining the other end of the scale, too, to avoid a different trap: spurious precision. You may be familiar with this concept when converting measurements between units, such as translating “about 1 metre” as “about 3.28084 feet”. I mentioned earlier the likelihood that your timestamps are not from a perfectly reliable source, and in many cases that means your string could be out by whole minutes. So when you say “Rowan posted this 1 minutes and 13 seconds ago”, there’s a reasonable chance it was actually over 3 minutes ago, or under 30 seconds. In that case, it might well make sense not only to never display seconds, but to display any value less than 5 minutes with something extra vague, like “just now” or “in the last few minutes”.

Think about Distributions

The Windows Explorer example highlights one of the hidden assumptions of most of these relative time displays: that the desired accuracy follows some kind of logarithmic curve as you move away from “now” or some other baseline. As things get further away from the baseline, we get progressively less accurate, grouping things into fewer, larger groups. Sometimes, this is explicit: selecting the older, larger groups is an act of “zooming out” from a point of focus within a large data set. Other times, it’s a consequence of relevance: whether an unread Facebook notification from 3 weeks ago was at 12:15 or 15:30 probably doesn’t bother you much. It might even fit a probability distribution: you’re unlikely to end up with only notifications labelled “3 weeks ago” because you’ll have either dismissed them or received newer ones pushing them off the screen.

But a lot of the time, this distribution doesn’t really fit the task at hand. If you’re viewing the version history of a frequently edited file, there’s no reason to suppose that the difference between “2 hours ago” and “3 hours ago” is actually more relevant than the difference between “5 weeks, 3 days, 2 hours ago” and “5 weeks, 3 days, 3 hours ago”. A common solution is just to “give up” on relative dates after a certain threshold, and revert to showing an absolute date and time; but that still leaves odd edges, like “21st Jan 23:59” being followed by “yesterday”.

This, of all the suggestions I’ve made, is the one that’s hardest to get right, but the solution may well be to mix absolute and relative times in one string to balance accuracy and readability. “Yesterday at 14:34” has the same accuracy as “1 day, 5 hours, and 16 minutes ago”, but is a lot easier to understand at a glance – which, I believe, was the intention of these formats all along.