Skip to content

Instantly share code, notes, and snippets.

@davidjb
Created January 9, 2013 01:59
Show Gist options
  • Save davidjb/4489920 to your computer and use it in GitHub Desktop.
Save davidjb/4489920 to your computer and use it in GitHub Desktop.
Improved humanisation for Ago
from datetime import datetime, timedelta
import ago
def human(date,
precision=2,
past_fmt="{} ago",
present_fmt="right now",
future_fmt="in {}",
present_tolerance=timedelta(0)):
"""Humanise a datetime object's difference between it and the time now.
Pass this method a datetime object, it will compute the difference between
it and ``datetime.now()`` and return this difference as a "human-readable"
string. Use the ``precision`` keyword argument to control the granularity
of the resulting string.
>>> from datetime import datetime, timedelta
>>> one_min_from_now = datetime.now() + timedelta(minutes=1.5)
>>> human(one_min_from_now, precision=1)
'in 1 minute'
>>> one_min_ago = datetime.now() - timedelta(minutes=1.5)
>>> human(one_min_ago, precision=1)
'1 minute ago'
This function will format the humanised difference according to the
string formats provided as keyword arguments for past, present, and
future dates.
>>> human(datetime.now() - timedelta(days=5, hours=2, minutes=30), precision=3, past_fmt="{} in the past")
'5 days, 2 hours, 30 minutes in the past'
>>> human(datetime.now() + timedelta(days=5, hours=2, minutes=30, seconds=30), precision=3, future_fmt="Coming up in {} time")
'Coming up in 5 days, 2 hours, 30 minutes time'
>>> human(one_min_ago, precision=1, present_tolerance=timedelta(minutes=2))
'right now'
>>> human(one_min_from_now, precision=1, present_tolerance=timedelta(minutes=2), present_fmt="very recently")
'very recently'
*Arguments/keywords*
date
The ``datetime`` object to compare to the current ``datetime`` (``datetime.now()``).
The timedelta between these is what is humanised. Required.
precision
The precision to use when humanising the resulting timedelta. This is
passed to ``delta2human``. Default: 2.
past_fmt
String to be formatted if the date is earlier than the current
time, outside of the given ``present_tolerance``. This string is
formatted using ``format()`` method and passed the result of
``delta2human``. Default: "{} ago".
present_fmt
String to be used if the date is considered the present. See
``present_tolerance`` for what is considered the 'present' time.
Default: "right now".
future_fmt
String to be formatted if the date is later than the current
time, outside of the given ``present_tolerance``. This string is
formatted using ``format()`` method and passed the result of
``delta2human``. Default: "{} ago".
present_tolerance
The ``datetime.timedelta`` margin to consider the difference
between ``date`` and the current ``datetime`` as being
'right now'. For example, this if set as
``datetime.timedelta(minutes=5)``, then dates between 5
minutes before and 5 minutes after now will be treated as the present.
Increasing the tolerance can useful for reducing precision to make
the result seem more 'human'.
This argument may be passed as ``dict`` or two-tuple of options used
for constructing a ``timedelta`` object.
Default: ``datetime.timedelta(0)`` (exact datetime match required).
"""
if not isinstance(present_tolerance, timedelta):
present_tolerance = timedelta(**dict(present_tolerance))
result = present_fmt
delta = date - datetime.now()
delta2human = lambda: ago.delta2human(abs(delta), precision=precision)
if delta > present_tolerance:
result = future_fmt.format(delta2human())
elif delta < -present_tolerance:
result = past_fmt.format(delta2human())
return result
if __name__ == "__main__":
import doctest
doctest.testmod()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment