"""Date/Time Helpers""" # Last synced with Rails copy at Revision 6080 on Feb 8th, 2007. # Note that the select_ tags are purposely not ported as they're very totally useless # and inefficient beyond comprehension. from datetime import datetime import time DEFAULT_PREFIX = 'date' def distance_of_time_in_words(from_time, to_time=0, include_seconds=False): """ Return approximate-time-distance string for two datetimes or ints. If times are integers, they are interpreted as seconds. Set ``include_seconds`` to True for more more detailed approximations when distance < 1 min, 29 secs Distances are reported based on the following table: 0 <-> 29 secs => less than a minute 30 secs <-> 1 min, 29 secs => 1 minute 1 min, 30 secs <-> 44 mins, 29 secs => [2..44] minutes 44 mins, 30 secs <-> 89 mins, 29 secs => about 1 hour 89 mins, 30 secs <-> 1 day minus 31 secs => about [2..24] hours 1 day minus 30 secs <-> 2 days minus 31 secs => 1 day 2 days minus 30 secs <-> 30 days minus 31 secs => [2..29] days 30 days minus 30 secs <-> 60 days minus 31 secs => about 1 month 60 days minus 30 secs <-> 1 yr minus 31 secs => [2..12] months 1 yr minus 30 secs <-> 2 yrs minus 31 secs => about 1 year 2 yrs minus 30 secs <-> max time or date => over [2..X] years With ``include_seconds`` set to True and the difference < 1 minute 29 seconds: 0-4 secs => less than 5 seconds 5-9 secs => less than 10 seconds 10-19 secs => less than 20 seconds 20-39 secs => half a minute 40-59 secs => less than a minute 60-89 secs => 1 minute Examples: >>> from datetime import datetime, timedelta >>> from_time = datetime.now() >>> distance_of_time_in_words(from_time, from_time + timedelta(minutes=50)) 'about 1 hour' >>> distance_of_time_in_words(from_time, from_time + timedelta(seconds=15)) 'less than a minute' >>> distance_of_time_in_words(from_time, from_time + timedelta(seconds=15), include_seconds=True) 'less than 20 seconds' Note: ``distance_of_time_in_words`` calculates one year as 365.25 days. """ if isinstance(from_time, int): from_time = time.time()+from_time else: from_time = time.mktime(from_time.timetuple()) if isinstance(to_time, int): to_time = time.time()+to_time else: to_time = time.mktime(to_time.timetuple()) distance_in_minutes = int(round(abs(to_time-from_time)/60)) distance_in_seconds = int(round(abs(to_time-from_time))) if distance_in_minutes <= 1: if include_seconds: for remainder in [5, 10, 20]: if distance_in_seconds < remainder: return "less than %s seconds" % remainder if distance_in_seconds < 40: return "half a minute" elif distance_in_seconds < 60: return "less than a minute" else: return "1 minute" else: if distance_in_minutes == 0: return "less than a minute" else: return "1 minute" elif distance_in_minutes < 45: return "%s minutes" % distance_in_minutes elif distance_in_minutes < 90: return "about 1 hour" elif distance_in_minutes < 1440: return "about %d hours" % (round(distance_in_minutes / 60.0)) elif distance_in_minutes < 2880: return "1 day" elif distance_in_minutes < 43220: return "%d days" % (round(distance_in_minutes / 1440)) elif distance_in_minutes < 86400: return "about 1 month" elif distance_in_minutes < 525600: return "%d months" % (round(distance_in_minutes / 43200)) elif distance_in_minutes < 1051200: return "about 1 year" else: return "over %d years" % (round(distance_in_minutes / 525600)) def time_ago_in_words(from_time, include_seconds=False): """ Return approximate-time-distance string for ``from_time`` til now. See ``distance_of_time_in_words`` -- same here, but ``to_time`` is fixed to ``datetime.now()``. """ return distance_of_time_in_words(from_time, datetime.now(), include_seconds) __all__ = ['distance_of_time_in_words', 'time_ago_in_words']