Ticket #7201: 7201.diff

File 7201.diff, 8.0 KB (added by Jeremy Carbaugh, 12 years ago)

Fixes timezone errors in timeuntil and timesince. Includes tests and documentation updates.

  • django/utils/timesince.py

     
    2727    )
    2828    # Convert datetime.date to datetime.datetime for comparison
    2929    if d.__class__ is not datetime.datetime:
    30         d = datetime.datetime(d.year, d.month, d.day)
    31     if now:
    32         t = now.timetuple()
     30        d = datetime.datetime(d.year, d.month, d.day)   
     31
     32    if now:
     33        # give a date without tzinfo the tzinfo of the other date   
     34        if d.tzinfo and not now.tzinfo:
     35            t = now.timetuple()
     36            now = datetime.datetime(t[0], t[1], t[2], t[3], t[4], t[5], tzinfo=LocalTimezone(d))
     37        elif not d.tzinfo and now.tzinfo:
     38            t = d.timetuple()
     39            d = datetime.datetime(t[0], t[1], t[2], t[3], t[4], t[5], tzinfo=LocalTimezone(now))
    3340    else:
    34         t = time.localtime()
    35     if d.tzinfo:
    36         tz = LocalTimezone(d)
    37     else:
    38         tz = None
    39     now = datetime.datetime(t[0], t[1], t[2], t[3], t[4], t[5], tzinfo=tz)
    40 
     41        # assigns now a tz only if d has tzinfo
     42        if d.tzinfo:
     43            now = datetime.datetime.now(LocalTimezone(d))
     44        else:
     45            now = datetime.datetime.now()
     46           
    4147    # ignore microsecond part of 'd' since we removed it from 'now'
    4248    delta = now - (d - datetime.timedelta(0, 0, d.microsecond))
    4349    since = delta.days * 24 * 60 * 60 + delta.seconds
     
    6268    Like timesince, but returns a string measuring the time until
    6369    the given time.
    6470    """
    65     if now == None:
    66         now = datetime.datetime.now()
     71    if not now:
     72        # assigns now a tz only if d has tzinfo
     73        if d.tzinfo:
     74            now = datetime.datetime.now(LocalTimezone(d))
     75        else:
     76            now = datetime.datetime.now()
    6777    return timesince(now, d)
  • django/template/defaultfilters.py

     
    649649
    650650def timeuntil(value, arg=None):
    651651    """Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
    652     from django.utils.timesince import timesince
     652    from django.utils.timesince import timeuntil
    653653    from datetime import datetime
    654654    if not value:
    655655        return u''
    656     if arg:
    657         return timesince(arg, value)
    658     return timesince(datetime.now(), value)
     656    return timeuntil(value, arg)
     657   
    659658timeuntil.is_safe = False
    660659
    661660###################
  • tests/regressiontests/templates/filters.py

     
    99
    1010from datetime import datetime, timedelta
    1111
    12 from django.utils.tzinfo import LocalTimezone
     12from django.utils.tzinfo import LocalTimezone, FixedOffset
    1313from django.utils.safestring import mark_safe
    1414
    1515# These two classes are used to test auto-escaping of __unicode__ output.
     
    2020class SafeClass:
    2121    def __unicode__(self):
    2222        return mark_safe(u'you > me')
    23 
     23       
    2424# RESULT SYNTAX --
    2525# 'template_name': ('template contents', 'context dict',
    2626#                   'expected string output' or Exception class)
    2727def get_filter_tests():
    2828    now = datetime.now()
    2929    now_tz = datetime.now(LocalTimezone(now))
     30    now_tz_i = datetime.now(FixedOffset((3 * 60) + 15)) # imaginary time zone
    3031    return {
    3132        # Default compare with datetime.now()
    3233        'filter-timesince01' : ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1 minute'),
     
    4041        # Check that timezone is respected
    4142        'filter-timesince06' : ('{{ a|timesince:b }}', {'a':now_tz + timedelta(hours=8), 'b':now_tz}, '8 hours'),
    4243
     44        # Ensures that differing timezones are calculated correctly
     45        'filter-timesince07' : ('{{ a|timesince }}', {'a': now}, '0 minutes'),
     46        'filter-timesince08' : ('{{ a|timesince }}', {'a': now_tz}, '0 minutes'),
     47        'filter-timesince09' : ('{{ a|timesince }}', {'a': now_tz_i}, '0 minutes'),
     48        'filter-timesince10' : ('{{ a|timesince:b }}', {'a': now, 'b': now_tz_i}, '0 minutes'),
     49        'filter-timesince11' : ('{{ a|timesince:b }}', {'a': now, 'b': now_tz}, '0 minutes'),
     50        'filter-timesince12' : ('{{ a|timesince:b }}', {'a': now_tz, 'b': now_tz_i}, '0 minutes'),
     51        'filter-timesince13' : ('{{ a|timesince:b }}', {'a': now_tz_i, 'b': now}, '0 minutes'),
     52        'filter-timesince14' : ('{{ a|timesince:b }}', {'a': now_tz_i, 'b': now_tz}, '0 minutes'),
     53       
    4354        # Default compare with datetime.now()
    4455        'filter-timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'),
    4556        'filter-timeuntil02' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(days=1, seconds = 10))}, '1 day'),
     
    4960        'filter-timeuntil04' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=1), 'b':now - timedelta(days=2)}, '1 day'),
    5061        'filter-timeuntil05' : ('{{ a|timeuntil:b }}', {'a':now - timedelta(days=2), 'b':now - timedelta(days=2, minutes=1)}, '1 minute'),
    5162
     63        # Ensures that differing timezones are calculated correctly
     64        'filter-timeuntil06' : ('{{ a|timeuntil }}', {'a': now_tz_i}, '0 minutes'),
     65        'filter-timeuntil07' : ('{{ a|timeuntil:b }}', {'a': now_tz_i, 'b': now_tz}, '0 minutes'),
     66
    5267        'filter-addslash01': ("{% autoescape off %}{{ a|addslashes }} {{ b|addslashes }}{% endautoescape %}", {"a": "<a>'", "b": mark_safe("<a>'")}, ur"<a>\' <a>\'"),
    5368        'filter-addslash02': ("{{ a|addslashes }} {{ b|addslashes }}", {"a": "<a>'", "b": mark_safe("<a>'")}, ur"&lt;a&gt;\&#39; <a>\'"),
    5469
  • tests/regressiontests/utils/timesince.py

     
    11"""
    22>>> from datetime import datetime, timedelta
    3 >>> from django.utils.timesince import timesince
     3>>> from django.utils.timesince import timesince, timeuntil
     4>>> from django.utils.tzinfo import LocalTimezone, FixedOffset
    45
    56>>> t = datetime(2007, 8, 14, 13, 46, 0)
    67
     
    7475u'0 minutes'
    7576>>> timesince(t, t-4*oneday-5*oneminute)
    7677u'0 minutes'
     78
     79# When using two different timezones.
     80>>> now = datetime.now()
     81>>> now_tz = datetime.now(LocalTimezone(now))
     82>>> now_tz_i = datetime.now(FixedOffset((3 * 60) + 15))
     83>>> timesince(now, now_tz)
     84u'0 minutes'
     85>>> timesince(now, now_tz_i)
     86u'0 minutes'
     87>>> timesince(now_tz, now_tz_i)
     88u'0 minutes'
     89>>> timesince(now_tz, now)
     90u'0 minutes'
     91>>> timeuntil(now, now_tz)
     92u'0 minutes'
     93>>> timeuntil(now, now_tz_i)
     94u'0 minutes'
     95>>> timeuntil(now_tz, now_tz_i)
     96u'0 minutes'
     97>>> timeuntil(now_tz, now)
     98u'0 minutes'
    7799"""
  • docs/templates.txt

     
    17631763June 2006, and ``comment_date`` is a date instance for 08:00 on 1 June 2006,
    17641764then ``{{ comment_date|timesince:blog_date }}`` would return "8 hours".
    17651765
     1766If the optional argument is used, a date will assume the timezone of the
     1767other if one has timezone information and the other does not.
     1768
    17661769Minutes is the smallest unit used, and "0 minutes" will be returned for any
    17671770date that is in the future relative to the comparison point.
    17681771
     
    17781781the comparison point (instead of *now*). If ``from_date`` contains 22 June
    177917822006, then ``{{ conference_date|timeuntil:from_date }}`` will return "1 week".
    17801783
     1784If the optional argument is used, a date will assume the timezone of the
     1785other if one has timezone information and the other does not.
     1786
    17811787Minutes is the smallest unit used, and "0 minutes" will be returned for any
    17821788date that is in the past relative to the comparison point.
    17831789
Back to Top