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:
parent
771481695f
commit
66203fc9ee
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
~~~~~
|
~~~~~
|
||||||
|
|
|
@ -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/'),
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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'
|
||||||
|
"""
|
Loading…
Reference in New Issue