Human readable 'Time Ago' questions

I’m looking at a way to give human readable formatting to a time difference. I had a quick go, but then realised it’s probably been done before.
A quick search came up with

def pretty_date(time=None, default_timezone=datetime.timezone.utc):
    Get a datetime object or a int() Epoch timestamp and return a
    pretty string like 'an hour ago', 'Yesterday', '3 months ago',
    'just now', etc

    # Assumes all timezone naive dates are UTC
    if time.tzinfo is None or time.tzinfo.utcoffset(time) is None:
        if default_timezone:
            time = time.replace(tzinfo=default_timezone)

    now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)

    if type(time) is int:
        diff = now - datetime.datetime.fromtimestamp(time)
    elif isinstance(time, datetime.datetime):
        diff = now - time
    elif not time:
        diff = now - now
    second_diff = diff.seconds
    day_diff = diff.days

    if day_diff < 0:
        return ''

    if day_diff == 0:
        if second_diff < 10:
            return "just now"
        if second_diff < 60:
            return str(second_diff) + " seconds ago"
        if second_diff < 120:
            return "a minute ago"
        if second_diff < 3600:
            return str(second_diff / 60) + " minutes ago"
        if second_diff < 7200:
            return "an hour ago"
        if second_diff < 86400:
            return str(second_diff / 3600) + " hours ago"
    if day_diff == 1:
        return "Yesterday"
    if day_diff < 7:
        return str(day_diff) + " days ago"
    if day_diff < 31:
        return str(day_diff / 7) + " weeks ago"
    if day_diff < 365:
        return str(day_diff / 30) + " months ago"
    return str(day_diff / 365) + " years ago"

I added the reference and fixed a fault. For my use case (time since last reading) I also need to round all the returned times as I get this was updated 4.643888888888889 hours ago

But I’ve also found arrow
an updated, up to date python3 module.

Has anyone used it, or have their own preference?

It looks from a quick read to be really nice, far more than I need. But I could move all datetime/time over to it.

thoughts ?


Arrow is a Python library that offers a sensible, human-friendly approach to creating, manipulating, formatting and converting dates, times, and timestamps. It implements and updates the datetime type, plugging gaps in functionality, and provides an intelligent module API that supports many common creation scenarios. Simply put, it helps you work with dates and times with fewer imports and a lot less code.

Arrow is heavily inspired by moment.js and requests.


Python’s standard library and some other low-level modules have near-complete date, time and time zone functionality but don’t work very well from a usability perspective:

  • Too many modules: datetime, time, calendar, dateutil, pytz and more
  • Too many types: date, time, datetime, tzinfo, timedelta, relativedelta, etc.
  • Time zones and timestamp conversions are verbose and unpleasant
  • Time zone naievety is the norm
  • Gaps in functionality: ISO-8601 parsing, timespans, humanization


  • Fully implemented, drop-in replacement for datetime
  • Supports Python 2.7, 3.5, 3.6, 3.7, and 3.8
  • Time zone-aware & UTC by default
  • Provides super-simple creation options for many common input scenarios
  • Updated .replace method with support for relative offsets, including weeks
  • Formats and parses strings automatically
  • Partial support for ISO-8601
  • Timezone conversion
  • Timestamp available as a property
  • Generates time spans, ranges, floors and ceilings in timeframes from year to microsecond
  • Humanizes and supports a growing list of contributed locales
  • Extensible for your own Arrow-derived types

Hey! I’ve run into this issue several times before.

Digging through my code archives reveals the following implementation in PHP I’ve used in Pepperminty Wiki:

 * Calculates the time since a particular timestamp and returns a
 * human-readable result.
 * @package core
 * @see The original source. No longer exists, maybe the wayback machine caught it :-(
 * @param	int		$time	The timestamp to convert.
 * @return	string	The time since the given timestamp as a human-readable string.
function human_time_since($time)
	return human_time(time() - $time);
 * Renders a given number of seconds as something that humans can understand more easily.
 * @package core
 * @param 	int		$seconds	The number of seconds to render.
 * @return	string	The rendered time.
function human_time($seconds)
	$tokens = array (
		31536000 => 'year',
		2592000 => 'month',
		604800 => 'week',
		86400 => 'day',
		3600 => 'hour',
		60 => 'minute',
		1 => 'second'
	foreach ($tokens as $unit => $text) {
		if ($seconds < $unit) continue;
		$numberOfUnits = floor($seconds / $unit);
		return $numberOfUnits.' '.$text.(($numberOfUnits>1)?'s':'').' ago';

Porting it to Python shouldn’t be too hard. I think I’ve got a C# or Javascript implementation lying around somewhere. If that’s useful, I’ll see if I can dig it up.

Example usage:

$d = new DateTime("2019-05-18 13:12:13"); // Initialise a DateTime
$result = human_time_since($d->getTimestamp());  //  Convert the DateTime to a unix timestamp
echo($result); // "1 month ago"