Fixed #7201 -- Fixed the timeuntil filter to work correctly with timezone-aware

times. Patch from Jeremy Carbaugh.

This is backwards incompatible in the sense that previously, if you tried to
compare timezone-aware and timezone-naive values, you got an incorrect result.
Now you get an empty string. So your previously incorrect code returns a
different incorrect result.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@8579 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2008-08-26 08:08:55 +00:00
parent 61957df17f
commit 3111d7f60b
6 changed files with 53 additions and 20 deletions

View File

@ -88,6 +88,7 @@ answer newbie questions, and generally made Django that much better:
Juan Manuel Caicedo <juan.manuel.caicedo@gmail.com>
Trevor Caira <trevor@caira.com>
Ricardo Javier Cárdenes Medina <ricardo.cardenes@gmail.com>
Jeremy Carbaugh <jcarbaugh@gmail.com>
Graham Carlyle <graham.carlyle@maplecroft.net>
Antonio Cavedoni <http://cavedoni.com/>
C8E

View File

@ -646,20 +646,24 @@ def timesince(value, arg=None):
from django.utils.timesince import timesince
if not value:
return u''
if arg:
return timesince(value, arg)
return timesince(value)
try:
if arg:
return timesince(value, arg)
return timesince(value)
except (ValueError, TypeError):
return u''
timesince.is_safe = False
def timeuntil(value, arg=None):
"""Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
from django.utils.timesince import timesince
from django.utils.timesince import timeuntil
from datetime import datetime
if not value:
return u''
if arg:
return timesince(arg, value)
return timesince(datetime.now(), value)
try:
return timeuntil(value, arg)
except (ValueError, TypeError):
return u''
timeuntil.is_safe = False
###################

View File

@ -28,15 +28,12 @@ def timesince(d, now=None):
# Convert datetime.date to datetime.datetime for comparison
if d.__class__ is not datetime.datetime:
d = datetime.datetime(d.year, d.month, d.day)
if now:
t = now.timetuple()
else:
t = time.localtime()
if d.tzinfo:
tz = LocalTimezone(d)
else:
tz = None
now = datetime.datetime(t[0], t[1], t[2], t[3], t[4], t[5], tzinfo=tz)
if not now:
if d.tzinfo:
now = datetime.datetime.now(LocalTimezone(d))
else:
now = datetime.datetime.now()
# ignore microsecond part of 'd' since we removed it from 'now'
delta = now - (d - datetime.timedelta(0, 0, d.microsecond))
@ -62,6 +59,9 @@ def timeuntil(d, now=None):
Like timesince, but returns a string measuring the time until
the given time.
"""
if now == None:
now = datetime.datetime.now()
if not now:
if d.tzinfo:
now = datetime.datetime.now(LocalTimezone(d))
else:
now = datetime.datetime.now()
return timesince(now, d)

View File

@ -1332,6 +1332,8 @@ 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 ``{{ blog_date|timesince:comment_date }}`` would return "8 hours".
Comparing offset-naive and offset-aware datetimes will return an empty string.
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.
@ -1349,6 +1351,8 @@ 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 "1 week".
Comparing offset-naive and offset-aware datetimes will return an empty string.
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.

View File

@ -9,7 +9,7 @@ consistent.
from datetime import datetime, timedelta
from django.utils.tzinfo import LocalTimezone
from django.utils.tzinfo import LocalTimezone, FixedOffset
from django.utils.safestring import mark_safe
# These two classes are used to test auto-escaping of __unicode__ output.
@ -27,6 +27,7 @@ class SafeClass:
def get_filter_tests():
now = datetime.now()
now_tz = datetime.now(LocalTimezone(now))
now_tz_i = datetime.now(FixedOffset((3 * 60) + 15)) # imaginary time zone
return {
# Default compare with datetime.now()
'filter-timesince01' : ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1 minute'),
@ -46,6 +47,14 @@ def get_filter_tests():
'filter-timesince09': ('{{ later|timesince }}', { 'later': now + timedelta(days=7) }, '0 minutes'),
'filter-timesince10': ('{{ later|timesince:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '0 minutes'),
# Ensures that differing timezones are calculated correctly
'filter-timesince11' : ('{{ a|timesince }}', {'a': now}, '0 minutes'),
'filter-timesince12' : ('{{ a|timesince }}', {'a': now_tz}, '0 minutes'),
'filter-timesince13' : ('{{ a|timesince }}', {'a': now_tz_i}, '0 minutes'),
'filter-timesince14' : ('{{ a|timesince:b }}', {'a': now_tz, 'b': now_tz_i}, '0 minutes'),
'filter-timesince15' : ('{{ a|timesince:b }}', {'a': now, 'b': now_tz_i}, ''),
'filter-timesince16' : ('{{ a|timesince:b }}', {'a': now_tz_i, 'b': now}, ''),
# Default compare with datetime.now()
'filter-timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'),
'filter-timeuntil02' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(days=1, seconds = 10))}, '1 day'),
@ -61,6 +70,9 @@ def get_filter_tests():
'filter-timeuntil08': ('{{ later|timeuntil }}', { 'later': now + timedelta(days=7, hours=1) }, '1 week'),
'filter-timeuntil09': ('{{ later|timeuntil:now }}', { 'now': now, 'later': now + timedelta(days=7) }, '1 week'),
# Ensures that differing timezones are calculated correctly
'filter-timeuntil10' : ('{{ a|timeuntil }}', {'a': now_tz_i}, '0 minutes'),
'filter-timeuntil11' : ('{{ a|timeuntil:b }}', {'a': now_tz_i, 'b': now_tz}, '0 minutes'),
'filter-addslash01': ("{% autoescape off %}{{ a|addslashes }} {{ b|addslashes }}{% endautoescape %}", {"a": "<a>'", "b": mark_safe("<a>'")}, ur"<a>\' <a>\'"),
'filter-addslash02': ("{{ a|addslashes }} {{ b|addslashes }}", {"a": "<a>'", "b": mark_safe("<a>'")}, ur"&lt;a&gt;\&#39; <a>\'"),

View File

@ -1,6 +1,7 @@
"""
>>> from datetime import datetime, timedelta
>>> from django.utils.timesince import timesince
>>> from django.utils.timesince import timesince, timeuntil
>>> from django.utils.tzinfo import LocalTimezone, FixedOffset
>>> t = datetime(2007, 8, 14, 13, 46, 0)
@ -74,4 +75,15 @@ u'0 minutes'
u'0 minutes'
>>> timesince(t, t-4*oneday-5*oneminute)
u'0 minutes'
# When using two different timezones.
>>> now = datetime.now()
>>> now_tz = datetime.now(LocalTimezone(now))
>>> now_tz_i = datetime.now(FixedOffset((3 * 60) + 15))
>>> timesince(now)
u'0 minutes'
>>> timesince(now_tz)
u'0 minutes'
>>> timeuntil(now_tz, now_tz_i)
u'0 minutes'
"""