|
| 1 | +fromdatetimeimporttzinfo,timedelta,datetime |
| 2 | + |
| 3 | +ZERO=timedelta(0) |
| 4 | +HOUR=timedelta(hours=1) |
| 5 | +SECOND=timedelta(seconds=1) |
| 6 | + |
| 7 | +# A class capturing the platform's idea of local time. |
| 8 | +# (May result in wrong values on historical times in |
| 9 | +# timezones where UTC offset and/or the DST rules had |
| 10 | +# changed in the past.) |
| 11 | +importtimeas_time |
| 12 | + |
| 13 | +STDOFFSET=timedelta(seconds=-_time.timezone) |
| 14 | +if_time.daylight: |
| 15 | +DSTOFFSET=timedelta(seconds=-_time.altzone) |
| 16 | +else: |
| 17 | +DSTOFFSET=STDOFFSET |
| 18 | + |
| 19 | +DSTDIFF=DSTOFFSET-STDOFFSET |
| 20 | + |
| 21 | +classLocalTimezone(tzinfo): |
| 22 | + |
| 23 | +deffromutc(self,dt): |
| 24 | +assertdt.tzinfoisself |
| 25 | +stamp= (dt-datetime(1970,1,1,tzinfo=self))//SECOND |
| 26 | +args=_time.localtime(stamp)[:6] |
| 27 | +dst_diff=DSTDIFF//SECOND |
| 28 | +# Detect fold |
| 29 | +fold= (args==_time.localtime(stamp-dst_diff)) |
| 30 | +returndatetime(*args,microsecond=dt.microsecond, |
| 31 | +tzinfo=self,fold=fold) |
| 32 | + |
| 33 | +defutcoffset(self,dt): |
| 34 | +ifself._isdst(dt): |
| 35 | +returnDSTOFFSET |
| 36 | +else: |
| 37 | +returnSTDOFFSET |
| 38 | + |
| 39 | +defdst(self,dt): |
| 40 | +ifself._isdst(dt): |
| 41 | +returnDSTDIFF |
| 42 | +else: |
| 43 | +returnZERO |
| 44 | + |
| 45 | +deftzname(self,dt): |
| 46 | +return_time.tzname[self._isdst(dt)] |
| 47 | + |
| 48 | +def_isdst(self,dt): |
| 49 | +tt= (dt.year,dt.month,dt.day, |
| 50 | +dt.hour,dt.minute,dt.second, |
| 51 | +dt.weekday(),0,0) |
| 52 | +stamp=_time.mktime(tt) |
| 53 | +tt=_time.localtime(stamp) |
| 54 | +returntt.tm_isdst>0 |
| 55 | + |
| 56 | +Local=LocalTimezone() |
| 57 | + |
| 58 | + |
| 59 | +# A complete implementation of current DST rules for major US time zones. |
| 60 | + |
| 61 | +deffirst_sunday_on_or_after(dt): |
| 62 | +days_to_go=6-dt.weekday() |
| 63 | +ifdays_to_go: |
| 64 | +dt+=timedelta(days_to_go) |
| 65 | +returndt |
| 66 | + |
| 67 | + |
| 68 | +# US DST Rules |
| 69 | +# |
| 70 | +# This is a simplified (i.e., wrong for a few cases) set of rules for US |
| 71 | +# DST start and end times. For a complete and up-to-date set of DST rules |
| 72 | +# and timezone definitions, visit the Olson Database (or try pytz): |
| 73 | +# http://www.twinsun.com/tz/tz-link.htm |
| 74 | +# https://sourceforge.net/projects/pytz/ (might not be up-to-date) |
| 75 | +# |
| 76 | +# In the US, since 2007, DST starts at 2am (standard time) on the second |
| 77 | +# Sunday in March, which is the first Sunday on or after Mar 8. |
| 78 | +DSTSTART_2007=datetime(1,3,8,2) |
| 79 | +# and ends at 2am (DST time) on the first Sunday of Nov. |
| 80 | +DSTEND_2007=datetime(1,11,1,2) |
| 81 | +# From 1987 to 2006, DST used to start at 2am (standard time) on the first |
| 82 | +# Sunday in April and to end at 2am (DST time) on the last |
| 83 | +# Sunday of October, which is the first Sunday on or after Oct 25. |
| 84 | +DSTSTART_1987_2006=datetime(1,4,1,2) |
| 85 | +DSTEND_1987_2006=datetime(1,10,25,2) |
| 86 | +# From 1967 to 1986, DST used to start at 2am (standard time) on the last |
| 87 | +# Sunday in April (the one on or after April 24) and to end at 2am (DST time) |
| 88 | +# on the last Sunday of October, which is the first Sunday |
| 89 | +# on or after Oct 25. |
| 90 | +DSTSTART_1967_1986=datetime(1,4,24,2) |
| 91 | +DSTEND_1967_1986=DSTEND_1987_2006 |
| 92 | + |
| 93 | +defus_dst_range(year): |
| 94 | +# Find start and end times for US DST. For years before 1967, return |
| 95 | +# start = end for no DST. |
| 96 | +if2006<year: |
| 97 | +dststart,dstend=DSTSTART_2007,DSTEND_2007 |
| 98 | +elif1986<year<2007: |
| 99 | +dststart,dstend=DSTSTART_1987_2006,DSTEND_1987_2006 |
| 100 | +elif1966<year<1987: |
| 101 | +dststart,dstend=DSTSTART_1967_1986,DSTEND_1967_1986 |
| 102 | +else: |
| 103 | +return (datetime(year,1,1), )*2 |
| 104 | + |
| 105 | +start=first_sunday_on_or_after(dststart.replace(year=year)) |
| 106 | +end=first_sunday_on_or_after(dstend.replace(year=year)) |
| 107 | +returnstart,end |
| 108 | + |
| 109 | + |
| 110 | +classUSTimeZone(tzinfo): |
| 111 | + |
| 112 | +def__init__(self,hours,reprname,stdname,dstname): |
| 113 | +self.stdoffset=timedelta(hours=hours) |
| 114 | +self.reprname=reprname |
| 115 | +self.stdname=stdname |
| 116 | +self.dstname=dstname |
| 117 | + |
| 118 | +def__repr__(self): |
| 119 | +returnself.reprname |
| 120 | + |
| 121 | +deftzname(self,dt): |
| 122 | +ifself.dst(dt): |
| 123 | +returnself.dstname |
| 124 | +else: |
| 125 | +returnself.stdname |
| 126 | + |
| 127 | +defutcoffset(self,dt): |
| 128 | +returnself.stdoffset+self.dst(dt) |
| 129 | + |
| 130 | +defdst(self,dt): |
| 131 | +ifdtisNoneordt.tzinfoisNone: |
| 132 | +# An exception may be sensible here, in one or both cases. |
| 133 | +# It depends on how you want to treat them. The default |
| 134 | +# fromutc() implementation (called by the default astimezone() |
| 135 | +# implementation) passes a datetime with dt.tzinfo is self. |
| 136 | +returnZERO |
| 137 | +assertdt.tzinfoisself |
| 138 | +start,end=us_dst_range(dt.year) |
| 139 | +# Can't compare naive to aware objects, so strip the timezone from |
| 140 | +# dt first. |
| 141 | +dt=dt.replace(tzinfo=None) |
| 142 | +ifstart+HOUR<=dt<end-HOUR: |
| 143 | +# DST is in effect. |
| 144 | +returnHOUR |
| 145 | +ifend-HOUR<=dt<end: |
| 146 | +# Fold (an ambiguous hour): use dt.fold to disambiguate. |
| 147 | +returnZEROifdt.foldelseHOUR |
| 148 | +ifstart<=dt<start+HOUR: |
| 149 | +# Gap (a non-existent hour): reverse the fold rule. |
| 150 | +returnHOURifdt.foldelseZERO |
| 151 | +# DST is off. |
| 152 | +returnZERO |
| 153 | + |
| 154 | +deffromutc(self,dt): |
| 155 | +assertdt.tzinfoisself |
| 156 | +start,end=us_dst_range(dt.year) |
| 157 | +start=start.replace(tzinfo=self) |
| 158 | +end=end.replace(tzinfo=self) |
| 159 | +std_time=dt+self.stdoffset |
| 160 | +dst_time=std_time+HOUR |
| 161 | +ifend<=dst_time<end+HOUR: |
| 162 | +# Repeated hour |
| 163 | +returnstd_time.replace(fold=1) |
| 164 | +ifstd_time<startordst_time>=end: |
| 165 | +# Standard time |
| 166 | +returnstd_time |
| 167 | +ifstart<=std_time<end-HOUR: |
| 168 | +# Daylight saving time |
| 169 | +returndst_time |
| 170 | + |
| 171 | + |
| 172 | +Eastern=USTimeZone(-5,"Eastern","EST","EDT") |
| 173 | +Central=USTimeZone(-6,"Central","CST","CDT") |
| 174 | +Mountain=USTimeZone(-7,"Mountain","MST","MDT") |
| 175 | +Pacific=USTimeZone(-8,"Pacific","PST","PDT") |