jpayne@68
|
1 '''
|
jpayne@68
|
2 Reference tzinfo implementations from the Python docs.
|
jpayne@68
|
3 Used for testing against as they are only correct for the years
|
jpayne@68
|
4 1987 to 2006. Do not use these for real code.
|
jpayne@68
|
5 '''
|
jpayne@68
|
6
|
jpayne@68
|
7 from datetime import tzinfo, timedelta, datetime
|
jpayne@68
|
8 from pytz import HOUR, ZERO, UTC
|
jpayne@68
|
9
|
jpayne@68
|
10 __all__ = [
|
jpayne@68
|
11 'FixedOffset',
|
jpayne@68
|
12 'LocalTimezone',
|
jpayne@68
|
13 'USTimeZone',
|
jpayne@68
|
14 'Eastern',
|
jpayne@68
|
15 'Central',
|
jpayne@68
|
16 'Mountain',
|
jpayne@68
|
17 'Pacific',
|
jpayne@68
|
18 'UTC'
|
jpayne@68
|
19 ]
|
jpayne@68
|
20
|
jpayne@68
|
21
|
jpayne@68
|
22 # A class building tzinfo objects for fixed-offset time zones.
|
jpayne@68
|
23 # Note that FixedOffset(0, "UTC") is a different way to build a
|
jpayne@68
|
24 # UTC tzinfo object.
|
jpayne@68
|
25 class FixedOffset(tzinfo):
|
jpayne@68
|
26 """Fixed offset in minutes east from UTC."""
|
jpayne@68
|
27
|
jpayne@68
|
28 def __init__(self, offset, name):
|
jpayne@68
|
29 self.__offset = timedelta(minutes=offset)
|
jpayne@68
|
30 self.__name = name
|
jpayne@68
|
31
|
jpayne@68
|
32 def utcoffset(self, dt):
|
jpayne@68
|
33 return self.__offset
|
jpayne@68
|
34
|
jpayne@68
|
35 def tzname(self, dt):
|
jpayne@68
|
36 return self.__name
|
jpayne@68
|
37
|
jpayne@68
|
38 def dst(self, dt):
|
jpayne@68
|
39 return ZERO
|
jpayne@68
|
40
|
jpayne@68
|
41
|
jpayne@68
|
42 import time as _time
|
jpayne@68
|
43
|
jpayne@68
|
44 STDOFFSET = timedelta(seconds=-_time.timezone)
|
jpayne@68
|
45 if _time.daylight:
|
jpayne@68
|
46 DSTOFFSET = timedelta(seconds=-_time.altzone)
|
jpayne@68
|
47 else:
|
jpayne@68
|
48 DSTOFFSET = STDOFFSET
|
jpayne@68
|
49
|
jpayne@68
|
50 DSTDIFF = DSTOFFSET - STDOFFSET
|
jpayne@68
|
51
|
jpayne@68
|
52
|
jpayne@68
|
53 # A class capturing the platform's idea of local time.
|
jpayne@68
|
54 class LocalTimezone(tzinfo):
|
jpayne@68
|
55
|
jpayne@68
|
56 def utcoffset(self, dt):
|
jpayne@68
|
57 if self._isdst(dt):
|
jpayne@68
|
58 return DSTOFFSET
|
jpayne@68
|
59 else:
|
jpayne@68
|
60 return STDOFFSET
|
jpayne@68
|
61
|
jpayne@68
|
62 def dst(self, dt):
|
jpayne@68
|
63 if self._isdst(dt):
|
jpayne@68
|
64 return DSTDIFF
|
jpayne@68
|
65 else:
|
jpayne@68
|
66 return ZERO
|
jpayne@68
|
67
|
jpayne@68
|
68 def tzname(self, dt):
|
jpayne@68
|
69 return _time.tzname[self._isdst(dt)]
|
jpayne@68
|
70
|
jpayne@68
|
71 def _isdst(self, dt):
|
jpayne@68
|
72 tt = (dt.year, dt.month, dt.day,
|
jpayne@68
|
73 dt.hour, dt.minute, dt.second,
|
jpayne@68
|
74 dt.weekday(), 0, -1)
|
jpayne@68
|
75 stamp = _time.mktime(tt)
|
jpayne@68
|
76 tt = _time.localtime(stamp)
|
jpayne@68
|
77 return tt.tm_isdst > 0
|
jpayne@68
|
78
|
jpayne@68
|
79 Local = LocalTimezone()
|
jpayne@68
|
80
|
jpayne@68
|
81
|
jpayne@68
|
82 def first_sunday_on_or_after(dt):
|
jpayne@68
|
83 days_to_go = 6 - dt.weekday()
|
jpayne@68
|
84 if days_to_go:
|
jpayne@68
|
85 dt += timedelta(days_to_go)
|
jpayne@68
|
86 return dt
|
jpayne@68
|
87
|
jpayne@68
|
88
|
jpayne@68
|
89 # In the US, DST starts at 2am (standard time) on the first Sunday in April.
|
jpayne@68
|
90 DSTSTART = datetime(1, 4, 1, 2)
|
jpayne@68
|
91 # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct.
|
jpayne@68
|
92 # which is the first Sunday on or after Oct 25.
|
jpayne@68
|
93 DSTEND = datetime(1, 10, 25, 1)
|
jpayne@68
|
94
|
jpayne@68
|
95
|
jpayne@68
|
96 # A complete implementation of current DST rules for major US time zones.
|
jpayne@68
|
97 class USTimeZone(tzinfo):
|
jpayne@68
|
98
|
jpayne@68
|
99 def __init__(self, hours, reprname, stdname, dstname):
|
jpayne@68
|
100 self.stdoffset = timedelta(hours=hours)
|
jpayne@68
|
101 self.reprname = reprname
|
jpayne@68
|
102 self.stdname = stdname
|
jpayne@68
|
103 self.dstname = dstname
|
jpayne@68
|
104
|
jpayne@68
|
105 def __repr__(self):
|
jpayne@68
|
106 return self.reprname
|
jpayne@68
|
107
|
jpayne@68
|
108 def tzname(self, dt):
|
jpayne@68
|
109 if self.dst(dt):
|
jpayne@68
|
110 return self.dstname
|
jpayne@68
|
111 else:
|
jpayne@68
|
112 return self.stdname
|
jpayne@68
|
113
|
jpayne@68
|
114 def utcoffset(self, dt):
|
jpayne@68
|
115 return self.stdoffset + self.dst(dt)
|
jpayne@68
|
116
|
jpayne@68
|
117 def dst(self, dt):
|
jpayne@68
|
118 if dt is None or dt.tzinfo is None:
|
jpayne@68
|
119 # An exception may be sensible here, in one or both cases.
|
jpayne@68
|
120 # It depends on how you want to treat them. The default
|
jpayne@68
|
121 # fromutc() implementation (called by the default astimezone()
|
jpayne@68
|
122 # implementation) passes a datetime with dt.tzinfo is self.
|
jpayne@68
|
123 return ZERO
|
jpayne@68
|
124 assert dt.tzinfo is self
|
jpayne@68
|
125
|
jpayne@68
|
126 # Find first Sunday in April & the last in October.
|
jpayne@68
|
127 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
|
jpayne@68
|
128 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
|
jpayne@68
|
129
|
jpayne@68
|
130 # Can't compare naive to aware objects, so strip the timezone from
|
jpayne@68
|
131 # dt first.
|
jpayne@68
|
132 if start <= dt.replace(tzinfo=None) < end:
|
jpayne@68
|
133 return HOUR
|
jpayne@68
|
134 else:
|
jpayne@68
|
135 return ZERO
|
jpayne@68
|
136
|
jpayne@68
|
137 Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
|
jpayne@68
|
138 Central = USTimeZone(-6, "Central", "CST", "CDT")
|
jpayne@68
|
139 Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
|
jpayne@68
|
140 Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
|