Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #20693 -- Add timezone support to built-in time filter.

Modified django.utils.dateformat module, moving __init__() method and
timezone-related format methods from DateFormat class to TimeFormat
base class. Modified timezone-related format methods to return an
empty string when timezone is inappropriate for input value.
  • Loading branch information...
commit dd3a883894a219bc6c69e556c694734ab82b33e9 1 parent fa57266
@wsmith323 wsmith323 authored
View
133 django/utils/dateformat.py
@@ -38,8 +38,19 @@ def format(self, formatstr):
return ''.join(pieces)
class TimeFormat(Formatter):
- def __init__(self, t):
- self.data = t
+
+ def __init__(self, obj):
+ self.data = obj
+ self.timezone = None
+
+ # We only support timezone when formatting datetime objects,
+ # not date objects (timezone information not appropriate),
+ # or time objects (against established django policy).
+ if isinstance(obj, datetime.datetime):
+ if is_naive(obj):
+ self.timezone = LocalTimezone(obj)
+ else:
+ self.timezone = obj.tzinfo
def a(self):
"'a.m.' or 'p.m.'"
@@ -57,6 +68,25 @@ def B(self):
"Swatch Internet time"
raise NotImplementedError
+ def e(self):
+ """
+ Timezone name.
+
+ If timezone information is not available, this method returns
+ an empty string.
+ """
+ if not self.timezone:
+ return ""
+
+ try:
+ if hasattr(self.data, 'tzinfo') and self.data.tzinfo:
+ # Have to use tzinfo.tzname and not datetime.tzname
+ # because datatime.tzname does not expect Unicode
+ return self.data.tzinfo.tzname(self.data) or ""
+ except NotImplementedError:
+ pass
+ return ""
+
def f(self):
"""
Time, in 12-hour hours and minutes, with minutes left off if they're
@@ -92,6 +122,21 @@ def i(self):
"Minutes; i.e. '00' to '59'"
return '%02d' % self.data.minute
+ def O(self):
+ """
+ Difference to Greenwich time in hours; e.g. '+0200', '-0430'.
+
+ If timezone information is not available, this method returns
+ an empty string.
+ """
+ if not self.timezone:
+ return ""
+
+ seconds = self.Z()
+ sign = '-' if seconds < 0 else '+'
+ seconds = abs(seconds)
+ return "%s%02d%02d" % (sign, seconds // 3600, (seconds // 60) % 60)
+
def P(self):
"""
Time, in 12-hour hours, minutes and 'a.m.'/'p.m.', with minutes left off
@@ -109,24 +154,48 @@ def s(self):
"Seconds; i.e. '00' to '59'"
return '%02d' % self.data.second
+ def T(self):
+ """
+ Time zone of this machine; e.g. 'EST' or 'MDT'.
+
+ If timezone information is not available, this method returns
+ an empty string.
+ """
+ if not self.timezone:
+ return ""
+
+ name = self.timezone.tzname(self.data) if self.timezone else None
+ if name is None:
+ name = self.format('O')
+ return six.text_type(name)
+
def u(self):
"Microseconds; i.e. '000000' to '999999'"
return '%06d' %self.data.microsecond
+ def Z(self):
+ """
+ Time zone offset in seconds (i.e. '-43200' to '43200'). The offset for
+ timezones west of UTC is always negative, and for those east of UTC is
+ always positive.
+
+ If timezone information is not available, this method returns
+ an empty string.
+ """
+ if not self.timezone:
+ return ""
+
+ offset = self.timezone.utcoffset(self.data)
+ # `offset` is a datetime.timedelta. For negative values (to the west of
+ # UTC) only days can be negative (days=-1) and seconds are always
+ # positive. e.g. UTC-1 -> timedelta(days=-1, seconds=82800, microseconds=0)
+ # Positive offsets have days=0
+ return offset.days * 86400 + offset.seconds
+
class DateFormat(TimeFormat):
year_days = [None, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
- def __init__(self, dt):
- # Accepts either a datetime or date object.
- self.data = dt
- self.timezone = None
- if isinstance(dt, datetime.datetime):
- if is_naive(dt):
- self.timezone = LocalTimezone(dt)
- else:
- self.timezone = dt.tzinfo
-
def b(self):
"Month, textual, 3 letters, lowercase; e.g. 'jan'"
return MONTHS_3[self.data.month]
@@ -146,17 +215,6 @@ def D(self):
"Day of the week, textual, 3 letters; e.g. 'Fri'"
return WEEKDAYS_ABBR[self.data.weekday()]
- def e(self):
- "Timezone name if available"
- try:
- if hasattr(self.data, 'tzinfo') and self.data.tzinfo:
- # Have to use tzinfo.tzname and not datetime.tzname
- # because datatime.tzname does not expect Unicode
- return self.data.tzinfo.tzname(self.data) or ""
- except NotImplementedError:
- pass
- return ""
-
def E(self):
"Alternative month names as required by some locales. Proprietary extension."
return MONTHS_ALT[self.data.month]
@@ -204,13 +262,6 @@ def o(self):
"ISO 8601 year number matching the ISO week number (W)"
return self.data.isocalendar()[0]
- def O(self):
- "Difference to Greenwich time in hours; e.g. '+0200', '-0430'"
- seconds = self.Z()
- sign = '-' if seconds < 0 else '+'
- seconds = abs(seconds)
- return "%s%02d%02d" % (sign, seconds // 3600, (seconds // 60) % 60)
-
def r(self):
"RFC 2822 formatted date; e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'"
return self.format('D, j M Y H:i:s O')
@@ -232,13 +283,6 @@ def t(self):
"Number of days in the given month; i.e. '28' to '31'"
return '%02d' % calendar.monthrange(self.data.year, self.data.month)[1]
- def T(self):
- "Time zone of this machine; e.g. 'EST' or 'MDT'"
- name = self.timezone.tzname(self.data) if self.timezone else None
- if name is None:
- name = self.format('O')
- return six.text_type(name)
-
def U(self):
"Seconds since the Unix epoch (January 1 1970 00:00:00 GMT)"
if isinstance(self.data, datetime.datetime) and is_aware(self.data):
@@ -291,26 +335,13 @@ def z(self):
doy += 1
return doy
- def Z(self):
- """
- Time zone offset in seconds (i.e. '-43200' to '43200'). The offset for
- timezones west of UTC is always negative, and for those east of UTC is
- always positive.
- """
- if not self.timezone:
- return 0
- offset = self.timezone.utcoffset(self.data)
- # `offset` is a datetime.timedelta. For negative values (to the west of
- # UTC) only days can be negative (days=-1) and seconds are always
- # positive. e.g. UTC-1 -> timedelta(days=-1, seconds=82800, microseconds=0)
- # Positive offsets have days=0
- return offset.days * 86400 + offset.seconds
def format(value, format_string):
"Convenience function"
df = DateFormat(value)
return df.format(format_string)
+
def time_format(value, format_string):
"Convenience function"
tf = TimeFormat(value)
View
7 tests/template_tests/filters.py
@@ -360,6 +360,13 @@ def get_filter_tests():
# Ticket 19370: Make sure |date doesn't blow up on a midnight time object
'date08': (r'{{ t|date:"H:i" }}', {'t': time(0, 1)}, '00:01'),
'date09': (r'{{ t|date:"H:i" }}', {'t': time(0, 0)}, '00:00'),
+ # Ticket 20693: Add timezone support to built-in time template filter
+ 'time01': (r'{{ dt|time:"e:O:T:Z" }}', {'dt': now_tz_i}, '+0315:+0315:+0315:11700'),
+ 'time02': (r'{{ dt|time:"e:T" }}', {'dt': now}, ':' + now_tz.tzinfo.tzname(now_tz)),
+ 'time03': (r'{{ t|time:"P:e:O:T:Z" }}', {'t': time(4, 0, tzinfo=FixedOffset(30))}, '4 a.m.::::'),
+ 'time04': (r'{{ t|time:"P:e:O:T:Z" }}', {'t': time(4, 0)}, '4 a.m.::::'),
+ 'time05': (r'{{ d|time:"P:e:O:T:Z" }}', {'d': today}, ''),
+ 'time06': (r'{{ obj|time:"P:e:O:T:Z" }}', {'obj': 'non-datetime-value'}, ''),
# Tests for #11687 and #16676
'add01': (r'{{ i|add:"5" }}', {'i': 2000}, '2005'),
View
11 tests/utils_tests/test_dateformat.py
@@ -127,10 +127,16 @@ def test_timezones(self):
wintertime = datetime(2005, 10, 30, 4, 00)
timestamp = datetime(2008, 5, 19, 11, 45, 23, 123456)
+ # 3h30m to the west of UTC
+ tz = FixedOffset(-3*60 - 30)
+ aware_dt = datetime(2009, 5, 16, 5, 30, 30, tzinfo=tz)
+
if self.tz_tests:
self.assertEqual(dateformat.format(my_birthday, 'O'), '+0100')
self.assertEqual(dateformat.format(my_birthday, 'r'), 'Sun, 8 Jul 1979 22:00:00 +0100')
self.assertEqual(dateformat.format(my_birthday, 'T'), 'CET')
+ self.assertEqual(dateformat.format(my_birthday, 'e'), '')
+ self.assertEqual(dateformat.format(aware_dt, 'e'), '-0330')
self.assertEqual(dateformat.format(my_birthday, 'U'), '300315600')
self.assertEqual(dateformat.format(timestamp, 'u'), '123456')
self.assertEqual(dateformat.format(my_birthday, 'Z'), '3600')
@@ -140,7 +146,4 @@ def test_timezones(self):
self.assertEqual(dateformat.format(wintertime, 'O'), '+0100')
# Ticket #16924 -- We don't need timezone support to test this
- # 3h30m to the west of UTC
- tz = FixedOffset(-3*60 - 30)
- dt = datetime(2009, 5, 16, 5, 30, 30, tzinfo=tz)
- self.assertEqual(dateformat.format(dt, 'O'), '-0330')
+ self.assertEqual(dateformat.format(aware_dt, 'O'), '-0330')
Please sign in to comment.
Something went wrong with that request. Please try again.