Exibindo datas relativas úteis em Python

Ao criar interfaces, muitas vezes uma data formatada em iso não é o que seus usuários estão realmente procurando. Eles querem algo para refrescar sua memória para que possam dizer “Oh, esse é o arquivo que enviei ontem” em vez de “Esse é o arquivo que enviei em 3 de outubro … hmm, isso foi antes ou depois de eu sair de férias …”

De qualquer forma, usando a maravilhosa biblioteca datetime do Python (e timedelta), aqui está um código para fazer datas relativas realmente legais para você. (Também como uma essência aqui: https://gist.github.com/3503910 )

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)