Ticket #479: dateformat.patch

File dateformat.patch, 13.7 KB (added by Sune Kirkeby <sune.kirkeby@…>, 19 years ago)

New, improved and working patch for timezone-formatting in dateformat

  • django/utils/tzinfo.py

    ==== Patch <dateformat> level 1
    Source: d410f328-f502-0410-afc1-bf1322476e67:/django/patches/djtime:2758
    Target: d410f328-f502-0410-afc1-bf1322476e67:/django/trunk:2731
    Log:
     r2748@moria:  sune | 2005-10-13 08:38:55 +0200
     Existing patches.
     r2754@moria:  sune | 2005-10-13 10:37:00 +0200
     Revert timezone-parsing in db typecasts.
     r2755@moria:  sune | 2005-10-13 10:37:21 +0200
     Add dateformat unittests.
     r2756@moria:  sune | 2005-10-13 10:39:02 +0200
     Grab timezone-name from __init__ argument.
     r2757@moria:  sune | 2005-10-13 10:39:29 +0200
     Implement / fix all but "Swatch internet time" formats.
     r2758@moria:  sune | 2005-10-13 11:00:04 +0200
     Fix dateformat to handle escaped chars.
    
    === django/utils/tzinfo.py
    ==================================================================
     
     1"""Implementation of a tzinfo-classes for use with datetime.datetime."""
     2
     3import time
     4from datetime import timedelta, tzinfo
     5
     6ZERO = timedelta(0)
     7STDOFFSET = timedelta(seconds=-time.timezone)
     8DSTOFFSET = timedelta(seconds=-time.altzone)
     9DSTDIFF = DSTOFFSET - STDOFFSET
     10
     11class FixedOffset(tzinfo):
     12    """Fixed offset in minutes east from UTC."""
     13
     14    def __init__(self, offset):
     15        self.__offset = timedelta(minutes=offset)
     16        # FIXME -- Not really a name...
     17        self.__name = "%+03d%02d" % (offset / 60, offset % 60)
     18
     19    def __repr__(self):
     20        return self.__name
     21
     22    def utcoffset(self, dt):
     23        return self.__offset
     24
     25    def tzname(self, dt):
     26        return self.__name
     27
     28    def dst(self, dt):
     29        return ZERO
     30
     31class LocalTimezone(tzinfo):
     32    """Proxy timezone information from time module."""
     33    def __init__(self, dt):
     34        tzinfo.__init__(self, dt)
     35        self._tzname = time.tzname[self._isdst(dt)]
     36    def __repr__(self):
     37        return self._tzname
     38
     39    def utcoffset(self, dt):
     40        if self._isdst(dt):
     41            return DSTOFFSET
     42        else:
     43            return STDOFFSET
     44    def dst(self, dt):
     45        if self._isdst(dt):
     46            return DSTDIFF
     47        else:
     48            return ZERO
     49    def tzname(self, dt):
     50        return time.tzname[self._isdst(dt)]
     51    def _isdst(self, dt):
     52        tt = (dt.year, dt.month, dt.day,
     53              dt.hour, dt.minute, dt.second,
     54              dt.weekday(), 0, -1)
     55        stamp = time.mktime(tt)
     56        tt = time.localtime(stamp)
     57        return tt.tm_isdst > 0
  • django/utils/timesince.py

    === django/utils/timesince.py
    ==================================================================
     
    11import time, math, datetime
     2from tzinfo import LocalTimezone
    23
    34def timesince(d, now=None):
    45    """
     
    67    as a nicely formatted string, e.g "10 minutes"
    78    Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since
    89    """
    9     original = time.mktime(d.timetuple())
    1010    chunks = (
    1111      (60 * 60 * 24 * 365, 'year'),
    1212      (60 * 60 * 24 * 30, 'month'),
     
    1414      (60 * 60, 'hour'),
    1515      (60, 'minute')
    1616    )
    17     if not now:
    18         now = time.time()
    19     since = now - original
     17    if now:
     18        t = time.mktime(now)
     19    else:
     20        t = time.localtime()
     21    if d.tzinfo:
     22        tz = LocalTimezone()
     23    else:
     24        tz = None
     25    now = datetime.datetime(t[0], t[1], t[2], t[3], t[4], t[5], tzinfo=tz)
     26    delta = now - d
     27    since = delta.days * 24 * 60 * 60 + delta.seconds
    2028    # Crazy iteration syntax because we need i to be current index
    2129    for i, (seconds, name) in zip(range(len(chunks)), chunks):
    2230        count = math.floor(since / seconds)
  • django/utils/dateformat.py

    === django/utils/dateformat.py
    ==================================================================
     
    1313
    1414from calendar import isleap
    1515from dates import MONTHS, MONTHS_AP, WEEKDAYS
     16from tzinfo import LocalTimezone
     17import time
     18import re
    1619
     20re_formatchars = re.compile(r'(?<!\\)([aABdDfFgGhHiIjlLmMnNOPrsStTUwWyYzZ])')
     21split_formatstr = re_formatchars.split
     22re_escaped = re.compile(r'\\(.)')
     23
    1724class DateFormat:
    1825    year_days = [None, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
    1926
    2027    def __init__(self, d):
    2128        self.date = d
    2229
     30        self.timezone = getattr(self.date, 'tzinfo', None)
     31        if not self.timezone:
     32            self.timezone = LocalTimezone(self.date)
     33
    2334    def a(self):
    2435        "'a.m.' or 'p.m.'"
    2536        if self.date.hour > 11:
     
    8495
    8596    def I(self):
    8697        "'1' if Daylight Savings Time, '0' otherwise."
    87         raise NotImplementedError
     98        if self.timezone.dst(self.date):
     99            return '1'
     100        else:
     101            return '0'
    88102
    89103    def j(self):
    90104        "Day of the month without leading zeros; i.e. '1' to '31'"
     
    116130
    117131    def O(self):
    118132        "Difference to Greenwich time in hours; e.g. '+0200'"
    119         raise NotImplementedError
     133        tz = self.timezone.utcoffset(self.date)
     134        return "%+03d%02d" % (tz.seconds / 3600, (tz.seconds / 60) % 60)
    120135
    121136    def P(self):
    122137        """
     
    133148
    134149    def r(self):
    135150        "RFC 822 formatted date; e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'"
    136         raise NotImplementedError
     151        return self.format('D, j M Y H:i:s O')
    137152
    138153    def s(self):
    139154        "Seconds; i.e. '00' to '59'"
     
    158173
    159174    def T(self):
    160175        "Time zone of this machine; e.g. 'EST' or 'MDT'"
    161         raise NotImplementedError
     176        name = self.timezone.tzname(self.date)
     177        if name is None:
     178            name = self.format('O')
     179        return name
    162180
    163181    def U(self):
    164182        "Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)"
    165         raise NotImplementedError
     183        off = self.timezone.utcoffset(self.date)
     184        return int(time.mktime(self.date.timetuple())) + off.seconds * 60
    166185
    167186    def w(self):
    168187        "Day of the week, numeric, i.e. '0' (Sunday) to '6' (Saturday)"
    169         weekday = self.date.weekday()
    170         if weekday == 0:
    171             return 6
    172         return weekday - 1
     188        return (self.date.weekday() + 1) % 7
    173189
    174190    def W(self):
    175191        "ISO-8601 week number of year, weeks starting on Monday"
     
    197213                    week_number -= 1
    198214        return week_number
    199215
     216    def y(self):
     217        "Year, 2 digits; e.g. '99'"
     218        return str(self.date.year)[2:]
     219
    200220    def Y(self):
    201221        "Year, 4 digits; e.g. '1999'"
    202222        return self.date.year
    203223
    204     def y(self):
    205         "Year, 2 digits; e.g. '99'"
    206         return str(self.date.year)[2:]
    207 
    208224    def z(self):
    209225        "Day of the year; i.e. '0' to '365'"
    210226        doy = self.year_days[self.date.month] + self.date.day
     
    216232        """Time zone offset in seconds (i.e. '-43200' to '43200'). The offset
    217233        for timezones west of UTC is always negative, and for those east of UTC
    218234        is always positive."""
    219         raise NotImplementedError
     235        return self.timezone.utcoffset(self.date).seconds
    220236
    221237    def format(self, formatstr):
    222         result = ''
    223         for char in formatstr:
    224             try:
    225                 result += str(getattr(self, char)())
    226             except AttributeError:
    227                 result += char
    228         return result
     238        pieces = []
     239        for i, piece in enumerate(split_formatstr(formatstr)):
     240            if i % 2:
     241                pieces.append(str(getattr(self, piece)()))
     242            elif piece:
     243                pieces.append(re_escaped.sub(r'\1', piece))
     244        return ''.join(pieces)
    229245
    230246class TimeFormat:
    231247    def __init__(self, t):
  • tests/othertests/db_typecasts.py

    === tests/othertests/db_typecasts.py
    ==================================================================
     
    11# Unit tests for django.core.db.typecasts
    22
    33from django.core.db import typecasts
     4from django.utils.tzinfo import FixedOffset
    45import datetime
    56
    67TEST_CASES = {
  • tests/othertests/dateformat.py

    === tests/othertests/dateformat.py
    ==================================================================
     
     1"""
     2>>> format(my_birthday, '')
     3''
     4>>> format(my_birthday, 'a')
     5'p.m.'
     6>>> format(my_birthday, 'A')
     7'PM'
     8>>> format(my_birthday, 'j')
     9'7'
     10>>> format(my_birthday, 'l')
     11'Saturday'
     12>>> format(my_birthday, 'L')
     13'False'
     14>>> format(my_birthday, 'm')
     15'07'
     16>>> format(my_birthday, 'M')
     17'Jul'
     18>>> format(my_birthday, 'n')
     19'7'
     20>>> format(my_birthday, 'N')
     21'July'
     22>>> format(my_birthday, 'O')
     23'+0100'
     24>>> format(my_birthday, 'P')
     25'10 p.m.'
     26>>> format(my_birthday, 'r')
     27'Sat, 7 Jul 1979 22:00:00 +0100'
     28>>> format(my_birthday, 's')
     29'00'
     30>>> format(my_birthday, 'S')
     31'th'
     32>>> format(my_birthday, 't')
     33Traceback (most recent call last):
     34    ...
     35NotImplementedError
     36>>> format(my_birthday, 'T')
     37'CET'
     38>>> format(my_birthday, 'U')
     39'300445200'
     40>>> format(my_birthday, 'w')
     41'6'
     42>>> format(my_birthday, 'W')
     43'27'
     44>>> format(my_birthday, 'y')
     45'79'
     46>>> format(my_birthday, 'Y')
     47'1979'
     48>>> format(my_birthday, 'z')
     49'188'
     50>>> format(my_birthday, 'Z')
     51'3600'
     52
     53>>> format(summertime, 'I')
     54'1'
     55>>> format(summertime, 'O')
     56'+0200'
     57>>> format(wintertime, 'I')
     58'0'
     59>>> format(wintertime, 'O')
     60'+0100'
     61
     62>>> format(my_birthday, 'Y z \\C\\E\\T')
     63'1979 188 CET'
     64"""
     65from django.utils.dateformat import format
     66import datetime
     67
     68import os
     69import time
     70
     71os.environ['TZ'] = 'Europe/Copenhagen'
     72time.tzset()
     73
     74my_birthday = datetime.datetime(1979, 7, 7, 22, 00)
     75summertime = datetime.datetime(2005, 10, 30, 1, 00)
     76wintertime = datetime.datetime(2005, 10, 30, 4, 00)
Back to Top