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