Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #2675 -- Changed the `timeuntil` and `timesince` template filte…

…rs 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
  • Loading branch information...
commit 66203fc9ee7499bc331123678c7280873dd912f9 1 parent 7714816
@gdub gdub authored
View
14 django/utils/timesince.py
@@ -4,8 +4,15 @@
def timesince(d, now=None):
"""
- Takes two datetime objects and returns the time between then and now
- as a nicely formatted string, e.g "10 minutes"
+ Takes two datetime objects and returns the time between d and now
+ 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
"""
chunks = (
@@ -32,6 +39,9 @@ def timesince(d, now=None):
# ignore microsecond part of 'd' since we removed it from 'now'
delta = now - (d - datetime.timedelta(0, 0, d.microsecond))
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):
count = since // seconds
if count != 0:
View
10 docs/templates.txt
@@ -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,
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
~~~~~~~~~
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
``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
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
~~~~~
View
8 tests/regressiontests/templates/tests.py
@@ -771,6 +771,10 @@ def test_templates(self):
# Check that timezone is respected
'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 ##################################################
# Default compare with datetime.now()
'timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'),
@@ -781,6 +785,10 @@ def test_templates(self):
'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'),
+ # 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 ########################################################
# Successes
'url01' : ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'),
View
10 tests/regressiontests/utils/tests.py
@@ -6,6 +6,8 @@
from django.utils import html
+from timesince import timesince_tests
+
class TestUtilsHtml(TestCase):
def check_output(self, function, value, output=None):
@@ -113,3 +115,11 @@ def test_fix_ampersands(self):
)
for value, output in items:
self.check_output(f, value, output)
+
+__test__ = {
+ 'timesince_tests': timesince_tests,
+}
+
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()
View
77 tests/regressiontests/utils/timesince.py
@@ -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'
+"""
Please sign in to comment.
Something went wrong with that request. Please try again.