Fixed #2675 -- Changed the `timeuntil` and `timesince` template filters to display "0 minutes" when passed a past or future date respectively instead of "-1 years, 12 months". Thanks to nickefford for the patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6366 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Gary Wilson Jr 2007-09-17 04:50:12 +00:00
parent 771481695f
commit 66203fc9ee
5 changed files with 115 additions and 4 deletions

View File

@ -4,8 +4,15 @@ from django.utils.translation import ungettext, ugettext
def timesince(d, now=None): def timesince(d, now=None):
""" """
Takes two datetime objects and returns the time between then and now Takes two datetime objects and returns the time between d and now
as a nicely formatted string, e.g "10 minutes" as a nicely formatted string, e.g. "10 minutes". If d occurs after now,
then "0 minutes" is returned.
Units used are years, months, weeks, days, hours, and minutes.
Seconds and microseconds are ignored. Up to two adjacent units will be
displayed. For example, "2 weeks, 3 days" and "1 year, 3 months" are
possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not.
Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since
""" """
chunks = ( chunks = (
@ -32,6 +39,9 @@ def timesince(d, now=None):
# ignore microsecond part of 'd' since we removed it from 'now' # ignore microsecond part of 'd' since we removed it from 'now'
delta = now - (d - datetime.timedelta(0, 0, d.microsecond)) delta = now - (d - datetime.timedelta(0, 0, d.microsecond))
since = delta.days * 24 * 60 * 60 + delta.seconds since = delta.days * 24 * 60 * 60 + delta.seconds
if since <= 0:
# d is in the future compared to now, stop processing.
return u'0 ' + ugettext('minutes')
for i, (seconds, name) in enumerate(chunks): for i, (seconds, name) in enumerate(chunks):
count = since // seconds count = since // seconds
if count != 0: if count != 0:

View File

@ -1275,17 +1275,23 @@ For example, if ``blog_date`` is a date instance representing midnight on 1
June 2006, and ``comment_date`` is a date instance for 08:00 on 1 June 2006, June 2006, and ``comment_date`` is a date instance for 08:00 on 1 June 2006,
then ``{{ comment_date|timesince:blog_date }}`` would return "8 hours". then ``{{ comment_date|timesince:blog_date }}`` would return "8 hours".
Minutes is the smallest unit used, and "0 minutes" will be returned for any
date that is in the future relative to the comparison point.
timeuntil timeuntil
~~~~~~~~~ ~~~~~~~~~
Similar to ``timesince``, except that it measures the time from now until the Similar to ``timesince``, except that it measures the time from now until the
given date or datetime. For example, if today is 1 June 2006 and given date or datetime. For example, if today is 1 June 2006 and
``conference_date`` is a date instance holding 29 June 2006, then ``conference_date`` is a date instance holding 29 June 2006, then
``{{ conference_date|timeuntil }}`` will return "28 days". ``{{ conference_date|timeuntil }}`` will return "4 weeks".
Takes an optional argument that is a variable containing the date to use as Takes an optional argument that is a variable containing the date to use as
the comparison point (instead of *now*). If ``from_date`` contains 22 June the comparison point (instead of *now*). If ``from_date`` contains 22 June
2006, then ``{{ conference_date|timeuntil:from_date }}`` will return "7 days". 2006, then ``{{ conference_date|timeuntil:from_date }}`` will return "1 week".
Minutes is the smallest unit used, and "0 minutes" will be returned for any
date that is in the past relative to the comparison point.
title title
~~~~~ ~~~~~

View File

@ -771,6 +771,10 @@ class Templates(unittest.TestCase):
# Check that timezone is respected # Check that timezone is respected
'timesince06' : ('{{ a|timesince:b }}', {'a':NOW_tz + timedelta(hours=8), 'b':NOW_tz}, '8 hours'), 'timesince06' : ('{{ a|timesince:b }}', {'a':NOW_tz + timedelta(hours=8), 'b':NOW_tz}, '8 hours'),
# Check times in the future.
'timesince07' : ('{{ a|timesince }}', {'a':datetime.now() + timedelta(minutes=1, seconds=10)}, '0 minutes'),
'timesince08' : ('{{ a|timesince }}', {'a':datetime.now() + timedelta(days=1, minutes=1)}, '0 minutes'),
### TIMEUNTIL TAG ################################################## ### TIMEUNTIL TAG ##################################################
# Default compare with datetime.now() # Default compare with datetime.now()
'timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'), 'timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'),
@ -781,6 +785,10 @@ class Templates(unittest.TestCase):
'timeuntil04' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=1), 'b':NOW - timedelta(days=2)}, '1 day'), 'timeuntil04' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=1), 'b':NOW - timedelta(days=2)}, '1 day'),
'timeuntil05' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=2), 'b':NOW - timedelta(days=2, minutes=1)}, '1 minute'), 'timeuntil05' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=2), 'b':NOW - timedelta(days=2, minutes=1)}, '1 minute'),
# Check times in the past.
'timeuntil07' : ('{{ a|timeuntil }}', {'a':datetime.now() - timedelta(minutes=1, seconds=10)}, '0 minutes'),
'timeuntil08' : ('{{ a|timeuntil }}', {'a':datetime.now() - timedelta(days=1, minutes=1)}, '0 minutes'),
### URL TAG ######################################################## ### URL TAG ########################################################
# Successes # Successes
'url01' : ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'), 'url01' : ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'),

View File

@ -6,6 +6,8 @@ from unittest import TestCase
from django.utils import html from django.utils import html
from timesince import timesince_tests
class TestUtilsHtml(TestCase): class TestUtilsHtml(TestCase):
def check_output(self, function, value, output=None): def check_output(self, function, value, output=None):
@ -113,3 +115,11 @@ class TestUtilsHtml(TestCase):
) )
for value, output in items: for value, output in items:
self.check_output(f, value, output) self.check_output(f, value, output)
__test__ = {
'timesince_tests': timesince_tests,
}
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -0,0 +1,77 @@
timesince_tests = """
>>> from datetime import datetime, timedelta
>>> from django.utils.timesince import timesince
>>> t = datetime(2007, 8, 14, 13, 46, 0)
>>> onemicrosecond = timedelta(microseconds=1)
>>> onesecond = timedelta(seconds=1)
>>> oneminute = timedelta(minutes=1)
>>> onehour = timedelta(hours=1)
>>> oneday = timedelta(days=1)
>>> oneweek = timedelta(days=7)
>>> onemonth = timedelta(days=30)
>>> oneyear = timedelta(days=365)
# equal datetimes.
>>> timesince(t, t)
u'0 minutes'
# Microseconds and seconds are ignored.
>>> timesince(t, t+onemicrosecond)
u'0 minutes'
>>> timesince(t, t+onesecond)
u'0 minutes'
# Test other units.
>>> timesince(t, t+oneminute)
u'1 minute'
>>> timesince(t, t+onehour)
u'1 hour'
>>> timesince(t, t+oneday)
u'1 day'
>>> timesince(t, t+oneweek)
u'1 week'
>>> timesince(t, t+onemonth)
u'1 month'
>>> timesince(t, t+oneyear)
u'1 year'
# Test multiple units.
>>> timesince(t, t+2*oneday+6*onehour)
u'2 days, 6 hours'
>>> timesince(t, t+2*oneweek+2*oneday)
u'2 weeks, 2 days'
# If the two differing units aren't adjacent, only the first unit is displayed.
>>> timesince(t, t+2*oneweek+3*onehour+4*oneminute)
u'2 weeks'
>>> timesince(t, t+4*oneday+5*oneminute)
u'4 days'
# When the second date occurs before the first, we should always get 0 minutes.
>>> timesince(t, t-onemicrosecond)
u'0 minutes'
>>> timesince(t, t-onesecond)
u'0 minutes'
>>> timesince(t, t-oneminute)
u'0 minutes'
>>> timesince(t, t-onehour)
u'0 minutes'
>>> timesince(t, t-oneday)
u'0 minutes'
>>> timesince(t, t-oneweek)
u'0 minutes'
>>> timesince(t, t-onemonth)
u'0 minutes'
>>> timesince(t, t-oneyear)
u'0 minutes'
>>> timesince(t, t-2*oneday-6*onehour)
u'0 minutes'
>>> timesince(t, t-2*oneweek-2*oneday)
u'0 minutes'
>>> timesince(t, t-2*oneweek-3*onehour-4*oneminute)
u'0 minutes'
>>> timesince(t, t-4*oneday-5*oneminute)
u'0 minutes'
"""