@@ -1,68 +1,70 @@ from datetime import tzinfo, timedelta, datetime ZERO = timedelta(0) HOUR = timedelta(hours=1) SECOND = timedelta(seconds=1) import datetime as dt # A class capturing the platform's idea of local time. # (May result in wrong values on historical times in # timezones where UTC offset and/or the DST rules had # changed in the past.) import time as _time import time ZERO = dt.timedelta(0) HOUR = dt.timedelta(hours=1) SECOND = dt.timedelta(seconds=1) STDOFFSET = timedelta(seconds = -_time .timezone) if_time .daylight: DSTOFFSET = timedelta(seconds = -_time .altzone) STDOFFSET =dt. timedelta(seconds=-time .timezone) iftime .daylight: DSTOFFSET =dt. timedelta(seconds=-time .altzone) else: DSTOFFSET = STDOFFSET DSTDIFF = DSTOFFSET - STDOFFSET class LocalTimezone(tzinfo): def fromutc(self, dt): assert dt.tzinfo is self stamp = (dt - datetime(1970, 1, 1, tzinfo=self)) // SECOND args = _time.localtime(stamp)[:6] class LocalTimezone(dt.tzinfo): def fromutc(self, when): assert when.tzinfo is self stamp = (when - dt.datetime(1970, 1, 1, tzinfo=self)) // SECOND args = time.localtime(stamp)[:6] dst_diff = DSTDIFF // SECOND # Detect fold fold = (args ==_time .localtime(stamp - dst_diff)) return datetime(*args, microsecond=dt .microsecond, tzinfo=self, fold=fold) fold = (args ==time .localtime(stamp - dst_diff)) returndt. datetime(*args, microsecond=when .microsecond, tzinfo=self, fold=fold) def utcoffset(self,dt ): if self._isdst(dt ): def utcoffset(self,when ): if self._isdst(when ): return DSTOFFSET else: return STDOFFSET def dst(self,dt ): if self._isdst(dt ): def dst(self,when ): if self._isdst(when ): return DSTDIFF else: return ZERO def tzname(self,dt ): return_time .tzname[self._isdst(dt )] def tzname(self,when ): returntime .tzname[self._isdst(when )] def _isdst(self,dt ): tt = (dt .year,dt .month,dt .day, dt .hour,dt .minute,dt .second,dt .weekday(), 0, 0) stamp =_time .mktime(tt) tt =_time .localtime(stamp) def _isdst(self,when ): tt = (when .year,when .month,when .day, when .hour,when .minute,when .second,when .weekday(), 0, 0) stamp =time .mktime(tt) tt =time .localtime(stamp) return tt.tm_isdst > 0 Local = LocalTimezone() # A complete implementation of current DST rules for major US time zones. def first_sunday_on_or_after(dt ): days_to_go = 6 -dt .weekday() def first_sunday_on_or_after(when ): days_to_go = 6 -when .weekday() if days_to_go: dt += timedelta(days_to_go) returndt when +=dt. timedelta(days_to_go) returnwhen # US DST Rules Expand All @@ -75,21 +77,22 @@ def first_sunday_on_or_after(dt): # # In the US, since 2007, DST starts at 2am (standard time) on the second # Sunday in March, which is the first Sunday on or after Mar 8. DSTSTART_2007 = datetime(1, 3, 8, 2) DSTSTART_2007 =dt. datetime(1, 3, 8, 2) # and ends at 2am (DST time) on the first Sunday of Nov. DSTEND_2007 = datetime(1, 11, 1, 2) DSTEND_2007 =dt. datetime(1, 11, 1, 2) # From 1987 to 2006, DST used to start at 2am (standard time) on the first # Sunday in April and to end at 2am (DST time) on the last # Sunday of October, which is the first Sunday on or after Oct 25. DSTSTART_1987_2006 = datetime(1, 4, 1, 2) DSTEND_1987_2006 = datetime(1, 10, 25, 2) DSTSTART_1987_2006 =dt. datetime(1, 4, 1, 2) DSTEND_1987_2006 =dt. datetime(1, 10, 25, 2) # From 1967 to 1986, DST used to start at 2am (standard time) on the last # Sunday in April (the one on or after April 24) and to end at 2am (DST time) # on the last Sunday of October, which is the first Sunday # on or after Oct 25. DSTSTART_1967_1986 = datetime(1, 4, 24, 2) DSTSTART_1967_1986 =dt. datetime(1, 4, 24, 2) DSTEND_1967_1986 = DSTEND_1987_2006 def us_dst_range(year): # Find start and end times for US DST. For years before 1967, return # start = end for no DST. Expand All @@ -100,63 +103,63 @@ def us_dst_range(year): elif 1966 < year < 1987: dststart, dstend = DSTSTART_1967_1986, DSTEND_1967_1986 else: return (datetime(year, 1, 1), ) * 2 return (dt. datetime(year, 1, 1), ) * 2 start = first_sunday_on_or_after(dststart.replace(year=year)) end = first_sunday_on_or_after(dstend.replace(year=year)) return start, end class USTimeZone(tzinfo): class USTimeZone(dt. tzinfo): def __init__(self, hours, reprname, stdname, dstname): self.stdoffset = timedelta(hours=hours) self.stdoffset =dt. timedelta(hours=hours) self.reprname = reprname self.stdname = stdname self.dstname = dstname def __repr__(self): return self.reprname def tzname(self,dt ): if self.dst(dt ): def tzname(self,when ): if self.dst(when ): return self.dstname else: return self.stdname def utcoffset(self,dt ): return self.stdoffset + self.dst(dt ) def utcoffset(self,when ): return self.stdoffset + self.dst(when ) def dst(self,dt ): ifdt is None ordt .tzinfo is None: def dst(self,when ): ifwhen is None orwhen .tzinfo is None: # An exception may be sensible here, in one or both cases. # It depends on how you want to treat them. The default # fromutc() implementation (called by the default astimezone() # implementation) passes a datetime withdt .tzinfo is self. # implementation) passes a datetime withwhen .tzinfo is self. return ZERO assertdt .tzinfo is self start, end = us_dst_range(dt .year) assertwhen .tzinfo is self start, end = us_dst_range(when .year) # Can't compare naive to aware objects, so strip the timezone from #dt first. dt =dt .replace(tzinfo=None) if start + HOUR <=dt < end - HOUR: #when first. when =when .replace(tzinfo=None) if start + HOUR <=when < end - HOUR: # DST is in effect. return HOUR if end - HOUR <=dt < end: # Fold (an ambiguous hour): usedt .fold to disambiguate. return ZERO ifdt .fold else HOUR if start <=dt < start + HOUR: if end - HOUR <=when < end: # Fold (an ambiguous hour): usewhen .fold to disambiguate. return ZERO ifwhen .fold else HOUR if start <=when < start + HOUR: # Gap (a non-existent hour): reverse the fold rule. return HOUR ifdt .fold else ZERO return HOUR ifwhen .fold else ZERO # DST is off. return ZERO def fromutc(self,dt ): assertdt .tzinfo is self start, end = us_dst_range(dt .year) def fromutc(self,when ): assertwhen .tzinfo is self start, end = us_dst_range(when .year) start = start.replace(tzinfo=self) end = end.replace(tzinfo=self) std_time =dt + self.stdoffset std_time =when + self.stdoffset dst_time = std_time + HOUR if end <= dst_time < end + HOUR: # Repeated hour Expand Down