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