jpayne@68: ''' jpayne@68: Reference tzinfo implementations from the Python docs. jpayne@68: Used for testing against as they are only correct for the years jpayne@68: 1987 to 2006. Do not use these for real code. jpayne@68: ''' jpayne@68: jpayne@68: from datetime import tzinfo, timedelta, datetime jpayne@68: from pytz import HOUR, ZERO, UTC jpayne@68: jpayne@68: __all__ = [ jpayne@68: 'FixedOffset', jpayne@68: 'LocalTimezone', jpayne@68: 'USTimeZone', jpayne@68: 'Eastern', jpayne@68: 'Central', jpayne@68: 'Mountain', jpayne@68: 'Pacific', jpayne@68: 'UTC' jpayne@68: ] jpayne@68: jpayne@68: jpayne@68: # A class building tzinfo objects for fixed-offset time zones. jpayne@68: # Note that FixedOffset(0, "UTC") is a different way to build a jpayne@68: # UTC tzinfo object. jpayne@68: class FixedOffset(tzinfo): jpayne@68: """Fixed offset in minutes east from UTC.""" jpayne@68: jpayne@68: def __init__(self, offset, name): jpayne@68: self.__offset = timedelta(minutes=offset) jpayne@68: self.__name = name jpayne@68: jpayne@68: def utcoffset(self, dt): jpayne@68: return self.__offset jpayne@68: jpayne@68: def tzname(self, dt): jpayne@68: return self.__name jpayne@68: jpayne@68: def dst(self, dt): jpayne@68: return ZERO jpayne@68: jpayne@68: jpayne@68: import time as _time jpayne@68: jpayne@68: STDOFFSET = timedelta(seconds=-_time.timezone) jpayne@68: if _time.daylight: jpayne@68: DSTOFFSET = timedelta(seconds=-_time.altzone) jpayne@68: else: jpayne@68: DSTOFFSET = STDOFFSET jpayne@68: jpayne@68: DSTDIFF = DSTOFFSET - STDOFFSET jpayne@68: jpayne@68: jpayne@68: # A class capturing the platform's idea of local time. jpayne@68: class LocalTimezone(tzinfo): jpayne@68: jpayne@68: def utcoffset(self, dt): jpayne@68: if self._isdst(dt): jpayne@68: return DSTOFFSET jpayne@68: else: jpayne@68: return STDOFFSET jpayne@68: jpayne@68: def dst(self, dt): jpayne@68: if self._isdst(dt): jpayne@68: return DSTDIFF jpayne@68: else: jpayne@68: return ZERO jpayne@68: jpayne@68: def tzname(self, dt): jpayne@68: return _time.tzname[self._isdst(dt)] jpayne@68: jpayne@68: def _isdst(self, dt): jpayne@68: tt = (dt.year, dt.month, dt.day, jpayne@68: dt.hour, dt.minute, dt.second, jpayne@68: dt.weekday(), 0, -1) jpayne@68: stamp = _time.mktime(tt) jpayne@68: tt = _time.localtime(stamp) jpayne@68: return tt.tm_isdst > 0 jpayne@68: jpayne@68: Local = LocalTimezone() jpayne@68: jpayne@68: jpayne@68: def first_sunday_on_or_after(dt): jpayne@68: days_to_go = 6 - dt.weekday() jpayne@68: if days_to_go: jpayne@68: dt += timedelta(days_to_go) jpayne@68: return dt jpayne@68: jpayne@68: jpayne@68: # In the US, DST starts at 2am (standard time) on the first Sunday in April. jpayne@68: DSTSTART = datetime(1, 4, 1, 2) jpayne@68: # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct. jpayne@68: # which is the first Sunday on or after Oct 25. jpayne@68: DSTEND = datetime(1, 10, 25, 1) jpayne@68: jpayne@68: jpayne@68: # A complete implementation of current DST rules for major US time zones. jpayne@68: class USTimeZone(tzinfo): jpayne@68: jpayne@68: def __init__(self, hours, reprname, stdname, dstname): jpayne@68: self.stdoffset = timedelta(hours=hours) jpayne@68: self.reprname = reprname jpayne@68: self.stdname = stdname jpayne@68: self.dstname = dstname jpayne@68: jpayne@68: def __repr__(self): jpayne@68: return self.reprname jpayne@68: jpayne@68: def tzname(self, dt): jpayne@68: if self.dst(dt): jpayne@68: return self.dstname jpayne@68: else: jpayne@68: return self.stdname jpayne@68: jpayne@68: def utcoffset(self, dt): jpayne@68: return self.stdoffset + self.dst(dt) jpayne@68: jpayne@68: def dst(self, dt): jpayne@68: if dt is None or dt.tzinfo is None: jpayne@68: # An exception may be sensible here, in one or both cases. jpayne@68: # It depends on how you want to treat them. The default jpayne@68: # fromutc() implementation (called by the default astimezone() jpayne@68: # implementation) passes a datetime with dt.tzinfo is self. jpayne@68: return ZERO jpayne@68: assert dt.tzinfo is self jpayne@68: jpayne@68: # Find first Sunday in April & the last in October. jpayne@68: start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year)) jpayne@68: end = first_sunday_on_or_after(DSTEND.replace(year=dt.year)) jpayne@68: jpayne@68: # Can't compare naive to aware objects, so strip the timezone from jpayne@68: # dt first. jpayne@68: if start <= dt.replace(tzinfo=None) < end: jpayne@68: return HOUR jpayne@68: else: jpayne@68: return ZERO jpayne@68: jpayne@68: Eastern = USTimeZone(-5, "Eastern", "EST", "EDT") jpayne@68: Central = USTimeZone(-6, "Central", "CST", "CDT") jpayne@68: Mountain = USTimeZone(-7, "Mountain", "MST", "MDT") jpayne@68: Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")