Relative datetimes in python
def reltime(date, compare_to=None, at='@'): | |
r'''Takes a datetime and returns a relative representation of the | |
time. | |
:param date: The date to render relatively | |
:param compare_to: what to compare the date to. Defaults to datetime.now() | |
:param at: date/time separator. defaults to "@". "at" is also reasonable. | |
>>> from datetime import datetime, timedelta | |
>>> today = datetime(2050, 9, 2, 15, 00) | |
>>> earlier = datetime(2050, 9, 2, 12) | |
>>> reltime(earlier, today) | |
'today @ 12pm' | |
>>> yesterday = today - timedelta(1) | |
>>> reltime(yesterday, compare_to=today) | |
'yesterday @ 3pm' | |
>>> reltime(datetime(2050, 9, 1, 15, 32), today) | |
'yesterday @ 3:32pm' | |
>>> reltime(datetime(2050, 8, 31, 16), today) | |
'Wednesday @ 4pm (2 days ago)' | |
>>> reltime(datetime(2050, 8, 26, 14), today) | |
'last Friday @ 2pm (7 days ago)' | |
>>> reltime(datetime(2049, 9, 2, 12, 00), today) | |
'September 2nd, 2049 @ 12pm (last year)' | |
>>> today = datetime(2012, 8, 29, 13, 52) | |
>>> last_mon = datetime(2012, 8, 20, 15, 40, 55) | |
>>> reltime(last_mon, today) | |
'last Monday @ 3:40pm (9 days ago)' | |
''' | |
def ordinal(n): | |
r'''Returns a string ordinal representation of a number | |
Taken from: http://stackoverflow.com/a/739301/180718 | |
''' | |
if 10 <= n % 100 < 20: | |
return str(n) + 'th' | |
else: | |
return str(n) + {1 : 'st', 2 : 'nd', 3 : 'rd'}.get(n % 10, "th") | |
compare_to = compare_to or datetime.now() | |
if date > compare_to: | |
return NotImplementedError('reltime only handles dates in the past') | |
#get timediff values | |
diff = compare_to - date | |
if diff.seconds < 60 * 60 * 8: #less than a business day? | |
days_ago = diff.days | |
else: | |
days_ago = diff.days + 1 | |
months_ago = compare_to.month - date.month | |
years_ago = compare_to.year - date.year | |
weeks_ago = int(math.ceil(days_ago / 7.0)) | |
#get a non-zero padded 12-hour hour | |
hr = date.strftime('%I') | |
if hr.startswith('0'): | |
hr = hr[1:] | |
wd = compare_to.weekday() | |
#calculate the time string | |
if date.minute == 0: | |
time = '{0}{1}'.format(hr, date.strftime('%p').lower()) | |
else: | |
time = '{0}:{1}'.format(hr, date.strftime('%M%p').lower()) | |
#calculate the date string | |
if days_ago == 0: | |
datestr = 'today {at} {time}' | |
elif days_ago == 1: | |
datestr = 'yesterday {at} {time}' | |
elif (wd in (5, 6) and days_ago in (wd+1, wd+2)) or \ | |
wd + 3 <= days_ago <= wd + 8: | |
#this was determined by making a table of wd versus days_ago and | |
#divining a relationship based on everyday speech. This is somewhat | |
#subjective I guess! | |
datestr = 'last {weekday} {at} {time} ({days_ago} days ago)' | |
elif days_ago <= wd + 2: | |
datestr = '{weekday} {at} {time} ({days_ago} days ago)' | |
elif years_ago == 1: | |
datestr = '{month} {day}, {year} {at} {time} (last year)' | |
elif years_ago > 1: | |
datestr = '{month} {day}, {year} {at} {time} ({years_ago} years ago)' | |
elif months_ago == 1: | |
datestr = '{month} {day} {at} {time} (last month)' | |
elif months_ago > 1: | |
datestr = '{month} {day} {at} {time} ({months_ago} months ago)' | |
else: | |
#not last week, but not last month either | |
datestr = '{month} {day} {at} {time} ({days_ago} days ago)' | |
return datestr.format(time=time, | |
weekday=date.strftime('%A'), | |
day=ordinal(date.day), | |
days=diff.days, | |
days_ago=days_ago, | |
month=date.strftime('%B'), | |
years_ago=years_ago, | |
months_ago=months_ago, | |
weeks_ago=weeks_ago, | |
year=date.year, | |
at=at) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment