Commit 1014ba02 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Fixed debug view crash during autumn DST change.

This only happens if USE_TZ = False and pytz is installed (perhaps not
the most logical combination, but who am I to jugde?)

Refs #23714 which essentially fixed the same problem when USE_TZ = True.

Thanks Florian and Carl for insisting until I wrote a complete patch.
parent 16794721
Loading
Loading
Loading
Loading
+28 −6
Original line number Diff line number Diff line
@@ -136,6 +136,8 @@ class TimeFormat(Formatter):
            return ""

        seconds = self.Z()
        if seconds == "":
            return ""
        sign = '-' if seconds < 0 else '+'
        seconds = abs(seconds)
        return "%s%02d%02d" % (sign, seconds // 3600, (seconds // 60) % 60)
@@ -167,7 +169,14 @@ class TimeFormat(Formatter):
        if not self.timezone:
            return ""

        name = self.timezone.tzname(self.data) if self.timezone else None
        name = None
        try:
            name = self.timezone.tzname(self.data)
        except Exception:
            # pytz raises AmbiguousTimeError during the autumn DST change.
            # This happens mainly when __init__ receives a naive datetime
            # and sets self.timezone = get_default_timezone().
            pass
        if name is None:
            name = self.format('O')
        return six.text_type(name)
@@ -188,7 +197,14 @@ class TimeFormat(Formatter):
        if not self.timezone:
            return ""

        try:
            offset = self.timezone.utcoffset(self.data)
        except Exception:
            # pytz raises AmbiguousTimeError during the autumn DST change.
            # This happens mainly when __init__ receives a naive datetime
            # and sets self.timezone = get_default_timezone().
            return ""

        # `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)
@@ -228,10 +244,16 @@ class DateFormat(TimeFormat):

    def I(self):
        "'1' if Daylight Savings Time, '0' otherwise."
        try:
            if self.timezone and self.timezone.dst(self.data):
                return '1'
            else:
                return '0'
        except Exception:
            # pytz raises AmbiguousTimeError during the autumn DST change.
            # This happens mainly when __init__ receives a naive datetime
            # and sets self.timezone = get_default_timezone().
            return ''

    def j(self):
        "Day of the month without leading zeros; i.e. '1' to '31'"
+3 −0
Original line number Diff line number Diff line
@@ -9,4 +9,7 @@ Django 1.8.7 fixes several bugs in 1.8.6.
Bugfixes
========

* Fixed a crash of the debug view during the autumn DST change when
  :setting:`USE_TZ` is ``False`` and ``pytz`` is installed.

* ...
+17 −0
Original line number Diff line number Diff line
@@ -10,6 +10,11 @@ from django.utils.timezone import (
    get_default_timezone, get_fixed_timezone, make_aware, utc,
)

try:
    import pytz
except ImportError:
    pytz = None


@override_settings(TIME_ZONE='Europe/Copenhagen')
class DateFormatTests(SimpleTestCase):
@@ -29,6 +34,18 @@ class DateFormatTests(SimpleTestCase):
        dt = datetime(2009, 5, 16, 5, 30, 30)
        self.assertEqual(datetime.fromtimestamp(int(format(dt, 'U'))), dt)

    def test_naive_ambiguous_datetime(self):
        # dt is ambiguous in Europe/Copenhagen. LocalTimezone guesses the
        # offset (and gets it wrong 50% of the time) while pytz refuses the
        # temptation to guess. In any case, this shouldn't crash.
        dt = datetime(2015, 10, 25, 2, 30, 0)

        # Try all formatters that involve self.timezone.
        self.assertEqual(format(dt, 'I'), '0' if pytz is None else '')
        self.assertEqual(format(dt, 'O'), '+0100' if pytz is None else '')
        self.assertEqual(format(dt, 'T'), 'CET' if pytz is None else '')
        self.assertEqual(format(dt, 'Z'), '3600' if pytz is None else '')

    @requires_tz_support
    def test_datetime_with_local_tzinfo(self):
        ltz = get_default_timezone()