From 05c217d3ae21303cc4d5ccff241854caf44af317 Mon Sep 17 00:00:00 2001 From: niemeyer <> Date: Thu, 28 Feb 2008 04:52:22 +0000 Subject: - Fixed case where tzrange.utcoffset and tzrange.dst() might fail due to a date being used where a datetime was expected (reported and fixed by Lennart Regebro). - Prevent tzstr from introducing daylight timings in strings that didn't specify them (reported by Lennart Regebro). - Calls like gettz("GMT+3") and gettz("UTC-2") will now return the expected values, instead of the TZ variable behavior. --- NEWS | 16 +++++++++++++--- dateutil/parser.py | 4 +++- dateutil/tz.py | 23 ++++++++++++++++------- test.py | 25 ++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 12 deletions(-) diff --git a/NEWS b/NEWS index 6a80ca4..27fd1cc 100644 --- a/NEWS +++ b/NEWS @@ -1,10 +1,20 @@ Version 1.4 ----------- -- Fixed another precision problem on conversion of decimal seconds to - microseconds, as reported by Erik Brown. Now they're gone for real, - since it's not using floating point arithmetic anymore. +- Fixed another parser precision problem on conversion of decimal seconds + to microseconds, as reported by Erik Brown. Now these issues are gone + for real since it's not using floating point arithmetic anymore. +- Fixed case where tzrange.utcoffset and tzrange.dst() might fail due + to a date being used where a datetime was expected (reported and fixed + by Lennart Regebro). + +- Prevent tzstr from introducing daylight timings in strings that didn't + specify them (reported by Lennart Regebro). + +- Calls like gettz("GMT+3") and gettz("UTC-2") will now return the + expected values, instead of the TZ variable behavior. + Version 1.3 diff --git a/dateutil/parser.py b/dateutil/parser.py index b1b7cc2..5d824e4 100644 --- a/dateutil/parser.py +++ b/dateutil/parser.py @@ -741,6 +741,8 @@ class _tzparser(object): if (i < len_l and (l[i] in ('+', '-') or l[i][0] in "0123456789")): if l[i] in ('+', '-'): + # Yes, that's right. See the TZ variable + # documentation. signal = (1,-1)[l[i] == '+'] i += 1 else: @@ -863,7 +865,7 @@ class _tzparser(object): except (IndexError, ValueError, AssertionError): return None - + return res diff --git a/dateutil/tz.py b/dateutil/tz.py index 61fe2c1..0e28d6b 100644 --- a/dateutil/tz.py +++ b/dateutil/tz.py @@ -438,7 +438,7 @@ class tzfile(datetime.tzinfo): # The documentation says that utcoffset()-dst() must # be constant for every dt. - return self._find_ttinfo(dt, laststd=1).delta-tti.delta + return tti.delta-self._find_ttinfo(dt, laststd=1).delta # An alternative for that would be: # @@ -492,12 +492,12 @@ class tzrange(datetime.tzinfo): self._dst_offset = self._std_offset+datetime.timedelta(hours=+1) else: self._dst_offset = ZERO - if start is None: + if dstabbr and start is None: self._start_delta = relativedelta.relativedelta( hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) else: self._start_delta = start - if end is None: + if dstabbr and end is None: self._end_delta = relativedelta.relativedelta( hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) else: @@ -524,7 +524,7 @@ class tzrange(datetime.tzinfo): def _isdst(self, dt): if not self._start_delta: return False - year = datetime.date(dt.year,1,1) + year = datetime.datetime(dt.year,1,1) start = year+self._start_delta end = year+self._end_delta dt = dt.replace(tzinfo=None) @@ -563,6 +563,11 @@ class tzstr(tzrange): if res is None: raise ValueError, "unknown string format" + # Here we break the compatibility with the TZ variable handling. + # GMT-3 actually *means* the timezone -3. + if res.stdabbr in ("GMT", "UTC"): + res.stdoffset *= -1 + # We must initialize it first, since _delta() needs # _std_offset and _dst_offset set. Use False in start/end # to avoid building it two times. @@ -570,9 +575,13 @@ class tzstr(tzrange): res.dstabbr, res.dstoffset, start=False, end=False) - self._start_delta = self._delta(res.start) - if self._start_delta: - self._end_delta = self._delta(res.end, isend=1) + if not res.dstabbr: + self._start_delta = None + self._end_delta = None + else: + self._start_delta = self._delta(res.start) + if self._start_delta: + self._end_delta = self._delta(res.end, isend=1) def _delta(self, x, isend=0): kwargs = {} diff --git a/test.py b/test.py index c99efff..b7ad75e 100644 --- a/test.py +++ b/test.py @@ -3871,7 +3871,7 @@ END:VTIMEZONE tz = zoneinfo.gettz("EST5EDT") self.assertEqual(datetime(2003,4,6,1,59,tzinfo=tz).tzname(), "EST") self.assertEqual(datetime(2003,4,6,2,00,tzinfo=tz).tzname(), "EDT") - + def testZoneInfoFileEnd1(self): tz = zoneinfo.gettz("EST5EDT") self.assertEqual(datetime(2003,10,26,0,59,tzinfo=tz).tzname(), "EDT") @@ -3899,6 +3899,29 @@ END:VTIMEZONE tz = tzfile(StringIO(base64.decodestring(self.NEW_YORK))) self.assertEquals(datetime(2007,3,31,20,12).tzname(), None) + def testBrokenIsDstHandling(self): + # tzrange._isdst() was using a date() rather than a datetime(). + # Issue reported by Lennart Regebro. + dt = datetime(2007,8,6,4,10, tzinfo=tzutc()) + self.assertEquals(dt.astimezone(tz=gettz("GMT+2")), + datetime(2007,8,6,6,10, tzinfo=tzstr("GMT+2"))) + + def testGMTHasNoDaylight(self): + # tzstr("GMT+2") improperly considered daylight saving time. + # Issue reported by Lennart Regebro. + dt = datetime(2007,8,6,4,10) + self.assertEquals(gettz("GMT+2").dst(dt), timedelta(0)) + + def testGMTOffset(self): + # GMT and UTC offsets have inverted signal when compared to the + # usual TZ variable handling. + dt = datetime(2007,8,6,4,10, tzinfo=tzutc()) + self.assertEquals(dt.astimezone(tz=tzstr("GMT+2")), + datetime(2007,8,6,6,10, tzinfo=tzstr("GMT+2"))) + self.assertEquals(dt.astimezone(tz=gettz("UTC-2")), + datetime(2007,8,6,2,10, tzinfo=tzstr("UTC-2"))) + + if __name__ == "__main__": unittest.main() -- cgit v1.2.1