9 changed files with 3734 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||||
|
""" |
||||
|
Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net> |
||||
|
|
||||
|
This module offers extensions to the standard python 2.3+ |
||||
|
datetime module. |
||||
|
""" |
||||
|
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" |
||||
|
__license__ = "PSF License" |
||||
|
__version__ = "1.5" |
@ -0,0 +1,92 @@ |
|||||
|
""" |
||||
|
Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net> |
||||
|
|
||||
|
This module offers extensions to the standard python 2.3+ |
||||
|
datetime module. |
||||
|
""" |
||||
|
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" |
||||
|
__license__ = "PSF License" |
||||
|
|
||||
|
import datetime |
||||
|
|
||||
|
__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"] |
||||
|
|
||||
|
EASTER_JULIAN = 1 |
||||
|
EASTER_ORTHODOX = 2 |
||||
|
EASTER_WESTERN = 3 |
||||
|
|
||||
|
def easter(year, method=EASTER_WESTERN): |
||||
|
""" |
||||
|
This method was ported from the work done by GM Arts, |
||||
|
on top of the algorithm by Claus Tondering, which was |
||||
|
based in part on the algorithm of Ouding (1940), as |
||||
|
quoted in "Explanatory Supplement to the Astronomical |
||||
|
Almanac", P. Kenneth Seidelmann, editor. |
||||
|
|
||||
|
This algorithm implements three different easter |
||||
|
calculation methods: |
||||
|
|
||||
|
1 - Original calculation in Julian calendar, valid in |
||||
|
dates after 326 AD |
||||
|
2 - Original method, with date converted to Gregorian |
||||
|
calendar, valid in years 1583 to 4099 |
||||
|
3 - Revised method, in Gregorian calendar, valid in |
||||
|
years 1583 to 4099 as well |
||||
|
|
||||
|
These methods are represented by the constants: |
||||
|
|
||||
|
EASTER_JULIAN = 1 |
||||
|
EASTER_ORTHODOX = 2 |
||||
|
EASTER_WESTERN = 3 |
||||
|
|
||||
|
The default method is method 3. |
||||
|
|
||||
|
More about the algorithm may be found at: |
||||
|
|
||||
|
http://users.chariot.net.au/~gmarts/eastalg.htm |
||||
|
|
||||
|
and |
||||
|
|
||||
|
http://www.tondering.dk/claus/calendar.html |
||||
|
|
||||
|
""" |
||||
|
|
||||
|
if not (1 <= method <= 3): |
||||
|
raise ValueError, "invalid method" |
||||
|
|
||||
|
# g - Golden year - 1 |
||||
|
# c - Century |
||||
|
# h - (23 - Epact) mod 30 |
||||
|
# i - Number of days from March 21 to Paschal Full Moon |
||||
|
# j - Weekday for PFM (0=Sunday, etc) |
||||
|
# p - Number of days from March 21 to Sunday on or before PFM |
||||
|
# (-6 to 28 methods 1 & 3, to 56 for method 2) |
||||
|
# e - Extra days to add for method 2 (converting Julian |
||||
|
# date to Gregorian date) |
||||
|
|
||||
|
y = year |
||||
|
g = y % 19 |
||||
|
e = 0 |
||||
|
if method < 3: |
||||
|
# Old method |
||||
|
i = (19*g+15)%30 |
||||
|
j = (y+y//4+i)%7 |
||||
|
if method == 2: |
||||
|
# Extra dates to convert Julian to Gregorian date |
||||
|
e = 10 |
||||
|
if y > 1600: |
||||
|
e = e+y//100-16-(y//100-16)//4 |
||||
|
else: |
||||
|
# New method |
||||
|
c = y//100 |
||||
|
h = (c-c//4-(8*c+13)//25+19*g+15)%30 |
||||
|
i = h-(h//28)*(1-(h//28)*(29//(h+1))*((21-g)//11)) |
||||
|
j = (y+y//4+i+2-c+c//4)%7 |
||||
|
|
||||
|
# p can be from -6 to 56 corresponding to dates 22 March to 23 May |
||||
|
# (later dates apply to method 2, although 23 May never actually occurs) |
||||
|
p = i-j+e |
||||
|
d = 1+(p+27+(p+6)//40)%31 |
||||
|
m = 3+(p+26)//30 |
||||
|
return datetime.date(int(y),int(m),int(d)) |
||||
|
|
@ -0,0 +1,886 @@ |
|||||
|
# -*- coding:iso-8859-1 -*- |
||||
|
""" |
||||
|
Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net> |
||||
|
|
||||
|
This module offers extensions to the standard python 2.3+ |
||||
|
datetime module. |
||||
|
""" |
||||
|
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" |
||||
|
__license__ = "PSF License" |
||||
|
|
||||
|
import datetime |
||||
|
import string |
||||
|
import time |
||||
|
import sys |
||||
|
import os |
||||
|
|
||||
|
try: |
||||
|
from cStringIO import StringIO |
||||
|
except ImportError: |
||||
|
from StringIO import StringIO |
||||
|
|
||||
|
import relativedelta |
||||
|
import tz |
||||
|
|
||||
|
|
||||
|
__all__ = ["parse", "parserinfo"] |
||||
|
|
||||
|
|
||||
|
# Some pointers: |
||||
|
# |
||||
|
# http://www.cl.cam.ac.uk/~mgk25/iso-time.html |
||||
|
# http://www.iso.ch/iso/en/prods-services/popstds/datesandtime.html |
||||
|
# http://www.w3.org/TR/NOTE-datetime |
||||
|
# http://ringmaster.arc.nasa.gov/tools/time_formats.html |
||||
|
# http://search.cpan.org/author/MUIR/Time-modules-2003.0211/lib/Time/ParseDate.pm |
||||
|
# http://stein.cshl.org/jade/distrib/docs/java.text.SimpleDateFormat.html |
||||
|
|
||||
|
|
||||
|
class _timelex(object): |
||||
|
|
||||
|
def __init__(self, instream): |
||||
|
if isinstance(instream, basestring): |
||||
|
instream = StringIO(instream) |
||||
|
self.instream = instream |
||||
|
self.wordchars = ('abcdfeghijklmnopqrstuvwxyz' |
||||
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZ_' |
||||
|
'ßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ' |
||||
|
'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ') |
||||
|
self.numchars = '0123456789' |
||||
|
self.whitespace = ' \t\r\n' |
||||
|
self.charstack = [] |
||||
|
self.tokenstack = [] |
||||
|
self.eof = False |
||||
|
|
||||
|
def get_token(self): |
||||
|
if self.tokenstack: |
||||
|
return self.tokenstack.pop(0) |
||||
|
seenletters = False |
||||
|
token = None |
||||
|
state = None |
||||
|
wordchars = self.wordchars |
||||
|
numchars = self.numchars |
||||
|
whitespace = self.whitespace |
||||
|
while not self.eof: |
||||
|
if self.charstack: |
||||
|
nextchar = self.charstack.pop(0) |
||||
|
else: |
||||
|
nextchar = self.instream.read(1) |
||||
|
while nextchar == '\x00': |
||||
|
nextchar = self.instream.read(1) |
||||
|
if not nextchar: |
||||
|
self.eof = True |
||||
|
break |
||||
|
elif not state: |
||||
|
token = nextchar |
||||
|
if nextchar in wordchars: |
||||
|
state = 'a' |
||||
|
elif nextchar in numchars: |
||||
|
state = '0' |
||||
|
elif nextchar in whitespace: |
||||
|
token = ' ' |
||||
|
break # emit token |
||||
|
else: |
||||
|
break # emit token |
||||
|
elif state == 'a': |
||||
|
seenletters = True |
||||
|
if nextchar in wordchars: |
||||
|
token += nextchar |
||||
|
elif nextchar == '.': |
||||
|
token += nextchar |
||||
|
state = 'a.' |
||||
|
else: |
||||
|
self.charstack.append(nextchar) |
||||
|
break # emit token |
||||
|
elif state == '0': |
||||
|
if nextchar in numchars: |
||||
|
token += nextchar |
||||
|
elif nextchar == '.': |
||||
|
token += nextchar |
||||
|
state = '0.' |
||||
|
else: |
||||
|
self.charstack.append(nextchar) |
||||
|
break # emit token |
||||
|
elif state == 'a.': |
||||
|
seenletters = True |
||||
|
if nextchar == '.' or nextchar in wordchars: |
||||
|
token += nextchar |
||||
|
elif nextchar in numchars and token[-1] == '.': |
||||
|
token += nextchar |
||||
|
state = '0.' |
||||
|
else: |
||||
|
self.charstack.append(nextchar) |
||||
|
break # emit token |
||||
|
elif state == '0.': |
||||
|
if nextchar == '.' or nextchar in numchars: |
||||
|
token += nextchar |
||||
|
elif nextchar in wordchars and token[-1] == '.': |
||||
|
token += nextchar |
||||
|
state = 'a.' |
||||
|
else: |
||||
|
self.charstack.append(nextchar) |
||||
|
break # emit token |
||||
|
if (state in ('a.', '0.') and |
||||
|
(seenletters or token.count('.') > 1 or token[-1] == '.')): |
||||
|
l = token.split('.') |
||||
|
token = l[0] |
||||
|
for tok in l[1:]: |
||||
|
self.tokenstack.append('.') |
||||
|
if tok: |
||||
|
self.tokenstack.append(tok) |
||||
|
return token |
||||
|
|
||||
|
def __iter__(self): |
||||
|
return self |
||||
|
|
||||
|
def next(self): |
||||
|
token = self.get_token() |
||||
|
if token is None: |
||||
|
raise StopIteration |
||||
|
return token |
||||
|
|
||||
|
def split(cls, s): |
||||
|
return list(cls(s)) |
||||
|
split = classmethod(split) |
||||
|
|
||||
|
|
||||
|
class _resultbase(object): |
||||
|
|
||||
|
def __init__(self): |
||||
|
for attr in self.__slots__: |
||||
|
setattr(self, attr, None) |
||||
|
|
||||
|
def _repr(self, classname): |
||||
|
l = [] |
||||
|
for attr in self.__slots__: |
||||
|
value = getattr(self, attr) |
||||
|
if value is not None: |
||||
|
l.append("%s=%s" % (attr, `value`)) |
||||
|
return "%s(%s)" % (classname, ", ".join(l)) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return self._repr(self.__class__.__name__) |
||||
|
|
||||
|
|
||||
|
class parserinfo(object): |
||||
|
|
||||
|
# m from a.m/p.m, t from ISO T separator |
||||
|
JUMP = [" ", ".", ",", ";", "-", "/", "'", |
||||
|
"at", "on", "and", "ad", "m", "t", "of", |
||||
|
"st", "nd", "rd", "th"] |
||||
|
|
||||
|
WEEKDAYS = [("Mon", "Monday"), |
||||
|
("Tue", "Tuesday"), |
||||
|
("Wed", "Wednesday"), |
||||
|
("Thu", "Thursday"), |
||||
|
("Fri", "Friday"), |
||||
|
("Sat", "Saturday"), |
||||
|
("Sun", "Sunday")] |
||||
|
MONTHS = [("Jan", "January"), |
||||
|
("Feb", "February"), |
||||
|
("Mar", "March"), |
||||
|
("Apr", "April"), |
||||
|
("May", "May"), |
||||
|
("Jun", "June"), |
||||
|
("Jul", "July"), |
||||
|
("Aug", "August"), |
||||
|
("Sep", "September"), |
||||
|
("Oct", "October"), |
||||
|
("Nov", "November"), |
||||
|
("Dec", "December")] |
||||
|
HMS = [("h", "hour", "hours"), |
||||
|
("m", "minute", "minutes"), |
||||
|
("s", "second", "seconds")] |
||||
|
AMPM = [("am", "a"), |
||||
|
("pm", "p")] |
||||
|
UTCZONE = ["UTC", "GMT", "Z"] |
||||
|
PERTAIN = ["of"] |
||||
|
TZOFFSET = {} |
||||
|
|
||||
|
def __init__(self, dayfirst=False, yearfirst=False): |
||||
|
self._jump = self._convert(self.JUMP) |
||||
|
self._weekdays = self._convert(self.WEEKDAYS) |
||||
|
self._months = self._convert(self.MONTHS) |
||||
|
self._hms = self._convert(self.HMS) |
||||
|
self._ampm = self._convert(self.AMPM) |
||||
|
self._utczone = self._convert(self.UTCZONE) |
||||
|
self._pertain = self._convert(self.PERTAIN) |
||||
|
|
||||
|
self.dayfirst = dayfirst |
||||
|
self.yearfirst = yearfirst |
||||
|
|
||||
|
self._year = time.localtime().tm_year |
||||
|
self._century = self._year//100*100 |
||||
|
|
||||
|
def _convert(self, lst): |
||||
|
dct = {} |
||||
|
for i in range(len(lst)): |
||||
|
v = lst[i] |
||||
|
if isinstance(v, tuple): |
||||
|
for v in v: |
||||
|
dct[v.lower()] = i |
||||
|
else: |
||||
|
dct[v.lower()] = i |
||||
|
return dct |
||||
|
|
||||
|
def jump(self, name): |
||||
|
return name.lower() in self._jump |
||||
|
|
||||
|
def weekday(self, name): |
||||
|
if len(name) >= 3: |
||||
|
try: |
||||
|
return self._weekdays[name.lower()] |
||||
|
except KeyError: |
||||
|
pass |
||||
|
return None |
||||
|
|
||||
|
def month(self, name): |
||||
|
if len(name) >= 3: |
||||
|
try: |
||||
|
return self._months[name.lower()]+1 |
||||
|
except KeyError: |
||||
|
pass |
||||
|
return None |
||||
|
|
||||
|
def hms(self, name): |
||||
|
try: |
||||
|
return self._hms[name.lower()] |
||||
|
except KeyError: |
||||
|
return None |
||||
|
|
||||
|
def ampm(self, name): |
||||
|
try: |
||||
|
return self._ampm[name.lower()] |
||||
|
except KeyError: |
||||
|
return None |
||||
|
|
||||
|
def pertain(self, name): |
||||
|
return name.lower() in self._pertain |
||||
|
|
||||
|
def utczone(self, name): |
||||
|
return name.lower() in self._utczone |
||||
|
|
||||
|
def tzoffset(self, name): |
||||
|
if name in self._utczone: |
||||
|
return 0 |
||||
|
return self.TZOFFSET.get(name) |
||||
|
|
||||
|
def convertyear(self, year): |
||||
|
if year < 100: |
||||
|
year += self._century |
||||
|
if abs(year-self._year) >= 50: |
||||
|
if year < self._year: |
||||
|
year += 100 |
||||
|
else: |
||||
|
year -= 100 |
||||
|
return year |
||||
|
|
||||
|
def validate(self, res): |
||||
|
# move to info |
||||
|
if res.year is not None: |
||||
|
res.year = self.convertyear(res.year) |
||||
|
if res.tzoffset == 0 and not res.tzname or res.tzname == 'Z': |
||||
|
res.tzname = "UTC" |
||||
|
res.tzoffset = 0 |
||||
|
elif res.tzoffset != 0 and res.tzname and self.utczone(res.tzname): |
||||
|
res.tzoffset = 0 |
||||
|
return True |
||||
|
|
||||
|
|
||||
|
class parser(object): |
||||
|
|
||||
|
def __init__(self, info=None): |
||||
|
self.info = info or parserinfo() |
||||
|
|
||||
|
def parse(self, timestr, default=None, |
||||
|
ignoretz=False, tzinfos=None, |
||||
|
**kwargs): |
||||
|
if not default: |
||||
|
default = datetime.datetime.now().replace(hour=0, minute=0, |
||||
|
second=0, microsecond=0) |
||||
|
res = self._parse(timestr, **kwargs) |
||||
|
if res is None: |
||||
|
raise ValueError, "unknown string format" |
||||
|
repl = {} |
||||
|
for attr in ["year", "month", "day", "hour", |
||||
|
"minute", "second", "microsecond"]: |
||||
|
value = getattr(res, attr) |
||||
|
if value is not None: |
||||
|
repl[attr] = value |
||||
|
ret = default.replace(**repl) |
||||
|
if res.weekday is not None and not res.day: |
||||
|
ret = ret+relativedelta.relativedelta(weekday=res.weekday) |
||||
|
if not ignoretz: |
||||
|
if callable(tzinfos) or tzinfos and res.tzname in tzinfos: |
||||
|
if callable(tzinfos): |
||||
|
tzdata = tzinfos(res.tzname, res.tzoffset) |
||||
|
else: |
||||
|
tzdata = tzinfos.get(res.tzname) |
||||
|
if isinstance(tzdata, datetime.tzinfo): |
||||
|
tzinfo = tzdata |
||||
|
elif isinstance(tzdata, basestring): |
||||
|
tzinfo = tz.tzstr(tzdata) |
||||
|
elif isinstance(tzdata, int): |
||||
|
tzinfo = tz.tzoffset(res.tzname, tzdata) |
||||
|
else: |
||||
|
raise ValueError, "offset must be tzinfo subclass, " \ |
||||
|
"tz string, or int offset" |
||||
|
ret = ret.replace(tzinfo=tzinfo) |
||||
|
elif res.tzname and res.tzname in time.tzname: |
||||
|
ret = ret.replace(tzinfo=tz.tzlocal()) |
||||
|
elif res.tzoffset == 0: |
||||
|
ret = ret.replace(tzinfo=tz.tzutc()) |
||||
|
elif res.tzoffset: |
||||
|
ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset)) |
||||
|
return ret |
||||
|
|
||||
|
class _result(_resultbase): |
||||
|
__slots__ = ["year", "month", "day", "weekday", |
||||
|
"hour", "minute", "second", "microsecond", |
||||
|
"tzname", "tzoffset"] |
||||
|
|
||||
|
def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False): |
||||
|
info = self.info |
||||
|
if dayfirst is None: |
||||
|
dayfirst = info.dayfirst |
||||
|
if yearfirst is None: |
||||
|
yearfirst = info.yearfirst |
||||
|
res = self._result() |
||||
|
l = _timelex.split(timestr) |
||||
|
try: |
||||
|
|
||||
|
# year/month/day list |
||||
|
ymd = [] |
||||
|
|
||||
|
# Index of the month string in ymd |
||||
|
mstridx = -1 |
||||
|
|
||||
|
len_l = len(l) |
||||
|
i = 0 |
||||
|
while i < len_l: |
||||
|
|
||||
|
# Check if it's a number |
||||
|
try: |
||||
|
value_repr = l[i] |
||||
|
value = float(value_repr) |
||||
|
except ValueError: |
||||
|
value = None |
||||
|
|
||||
|
if value is not None: |
||||
|
# Token is a number |
||||
|
len_li = len(l[i]) |
||||
|
i += 1 |
||||
|
if (len(ymd) == 3 and len_li in (2, 4) |
||||
|
and (i >= len_l or (l[i] != ':' and |
||||
|
info.hms(l[i]) is None))): |
||||
|
# 19990101T23[59] |
||||
|
s = l[i-1] |
||||
|
res.hour = int(s[:2]) |
||||
|
if len_li == 4: |
||||
|
res.minute = int(s[2:]) |
||||
|
elif len_li == 6 or (len_li > 6 and l[i-1].find('.') == 6): |
||||
|
# YYMMDD or HHMMSS[.ss] |
||||
|
s = l[i-1] |
||||
|
if not ymd and l[i-1].find('.') == -1: |
||||
|
ymd.append(info.convertyear(int(s[:2]))) |
||||
|
ymd.append(int(s[2:4])) |
||||
|
ymd.append(int(s[4:])) |
||||
|
else: |
||||
|
# 19990101T235959[.59] |
||||
|
res.hour = int(s[:2]) |
||||
|
res.minute = int(s[2:4]) |
||||
|
res.second, res.microsecond = _parsems(s[4:]) |
||||
|
elif len_li == 8: |
||||
|
# YYYYMMDD |
||||
|
s = l[i-1] |
||||
|
ymd.append(int(s[:4])) |
||||
|
ymd.append(int(s[4:6])) |
||||
|
ymd.append(int(s[6:])) |
||||
|
elif len_li in (12, 14): |
||||
|
# YYYYMMDDhhmm[ss] |
||||
|
s = l[i-1] |
||||
|
ymd.append(int(s[:4])) |
||||
|
ymd.append(int(s[4:6])) |
||||
|
ymd.append(int(s[6:8])) |
||||
|
res.hour = int(s[8:10]) |
||||
|
res.minute = int(s[10:12]) |
||||
|
if len_li == 14: |
||||
|
res.second = int(s[12:]) |
||||
|
elif ((i < len_l and info.hms(l[i]) is not None) or |
||||
|
(i+1 < len_l and l[i] == ' ' and |
||||
|
info.hms(l[i+1]) is not None)): |
||||
|
# HH[ ]h or MM[ ]m or SS[.ss][ ]s |
||||
|
if l[i] == ' ': |
||||
|
i += 1 |
||||
|
idx = info.hms(l[i]) |
||||
|
while True: |
||||
|
if idx == 0: |
||||
|
res.hour = int(value) |
||||
|
if value%1: |
||||
|
res.minute = int(60*(value%1)) |
||||
|
elif idx == 1: |
||||
|
res.minute = int(value) |
||||
|
if value%1: |
||||
|
res.second = int(60*(value%1)) |
||||
|
elif idx == 2: |
||||
|
res.second, res.microsecond = \ |
||||
|
_parsems(value_repr) |
||||
|
i += 1 |
||||
|
if i >= len_l or idx == 2: |
||||
|
break |
||||
|
# 12h00 |
||||
|
try: |
||||
|
value_repr = l[i] |
||||
|
value = float(value_repr) |
||||
|
except ValueError: |
||||
|
break |
||||
|
else: |
||||
|
i += 1 |
||||
|
idx += 1 |
||||
|
if i < len_l: |
||||
|
newidx = info.hms(l[i]) |
||||
|
if newidx is not None: |
||||
|
idx = newidx |
||||
|
elif i+1 < len_l and l[i] == ':': |
||||
|
# HH:MM[:SS[.ss]] |
||||
|
res.hour = int(value) |
||||
|
i += 1 |
||||
|
value = float(l[i]) |
||||
|
res.minute = int(value) |
||||
|
if value%1: |
||||
|
res.second = int(60*(value%1)) |
||||
|
i += 1 |
||||
|
if i < len_l and l[i] == ':': |
||||
|
res.second, res.microsecond = _parsems(l[i+1]) |
||||
|
i += 2 |
||||
|
elif i < len_l and l[i] in ('-', '/', '.'): |
||||
|
sep = l[i] |
||||
|
ymd.append(int(value)) |
||||
|
i += 1 |
||||
|
if i < len_l and not info.jump(l[i]): |
||||
|
try: |
||||
|
# 01-01[-01] |
||||
|
ymd.append(int(l[i])) |
||||
|
except ValueError: |
||||
|
# 01-Jan[-01] |
||||
|
value = info.month(l[i]) |
||||
|
if value is not None: |
||||
|
ymd.append(value) |
||||
|
assert mstridx == -1 |
||||
|
mstridx = len(ymd)-1 |
||||
|
else: |
||||
|
return None |
||||
|
i += 1 |
||||
|
if i < len_l and l[i] == sep: |
||||
|
# We have three members |
||||
|
i += 1 |
||||
|
value = info.month(l[i]) |
||||
|
if value is not None: |
||||
|
ymd.append(value) |
||||
|
mstridx = len(ymd)-1 |
||||
|
assert mstridx == -1 |
||||
|
else: |
||||
|
ymd.append(int(l[i])) |
||||
|
i += 1 |
||||
|
elif i >= len_l or info.jump(l[i]): |
||||
|
if i+1 < len_l and info.ampm(l[i+1]) is not None: |
||||
|
# 12 am |
||||
|
res.hour = int(value) |
||||
|
if res.hour < 12 and info.ampm(l[i+1]) == 1: |
||||
|
res.hour += 12 |
||||
|
elif res.hour == 12 and info.ampm(l[i+1]) == 0: |
||||
|
res.hour = 0 |
||||
|
i += 1 |
||||
|
else: |
||||
|
# Year, month or day |
||||
|
ymd.append(int(value)) |
||||
|
i += 1 |
||||
|
elif info.ampm(l[i]) is not None: |
||||
|
# 12am |
||||
|
res.hour = int(value) |
||||
|
if res.hour < 12 and info.ampm(l[i]) == 1: |
||||
|
res.hour += 12 |
||||
|
elif res.hour == 12 and info.ampm(l[i]) == 0: |
||||
|
res.hour = 0 |
||||
|
i += 1 |
||||
|
elif not fuzzy: |
||||
|
return None |
||||
|
else: |
||||
|
i += 1 |
||||
|
continue |
||||
|
|
||||
|
# Check weekday |
||||
|
value = info.weekday(l[i]) |
||||
|
if value is not None: |
||||
|
res.weekday = value |
||||
|
i += 1 |
||||
|
continue |
||||
|
|
||||
|
# Check month name |
||||
|
value = info.month(l[i]) |
||||
|
if value is not None: |
||||
|
ymd.append(value) |
||||
|
assert mstridx == -1 |
||||
|
mstridx = len(ymd)-1 |
||||
|
i += 1 |
||||
|
if i < len_l: |
||||
|
if l[i] in ('-', '/'): |
||||
|
# Jan-01[-99] |
||||
|
sep = l[i] |
||||
|
i += 1 |
||||
|
ymd.append(int(l[i])) |
||||
|
i += 1 |
||||
|
if i < len_l and l[i] == sep: |
||||
|
# Jan-01-99 |
||||
|
i += 1 |
||||
|
ymd.append(int(l[i])) |
||||
|
i += 1 |
||||
|
elif (i+3 < len_l and l[i] == l[i+2] == ' ' |
||||
|
and info.pertain(l[i+1])): |
||||
|
# Jan of 01 |
||||
|
# In this case, 01 is clearly year |
||||
|
try: |
||||
|
value = int(l[i+3]) |
||||
|
except ValueError: |
||||
|
# Wrong guess |
||||
|
pass |
||||
|
else: |
||||
|
# Convert it here to become unambiguous |
||||
|
ymd.append(info.convertyear(value)) |
||||
|
i += 4 |
||||
|
continue |
||||
|
|
||||
|
# Check am/pm |
||||
|
value = info.ampm(l[i]) |
||||
|
if value is not None: |
||||
|
if value == 1 and res.hour < 12: |
||||
|
res.hour += 12 |
||||
|
elif value == 0 and res.hour == 12: |
||||
|
res.hour = 0 |
||||
|
i += 1 |
||||
|
continue |
||||
|
|
||||
|
# Check for a timezone name |
||||
|
if (res.hour is not None and len(l[i]) <= 5 and |
||||
|
res.tzname is None and res.tzoffset is None and |
||||
|
not [x for x in l[i] if x not in string.ascii_uppercase]): |
||||
|
res.tzname = l[i] |
||||
|
res.tzoffset = info.tzoffset(res.tzname) |
||||
|
i += 1 |
||||
|
|
||||
|
# Check for something like GMT+3, or BRST+3. Notice |
||||
|
# that it doesn't mean "I am 3 hours after GMT", but |
||||
|
# "my time +3 is GMT". If found, we reverse the |
||||
|
# logic so that timezone parsing code will get it |
||||
|
# right. |
||||
|
if i < len_l and l[i] in ('+', '-'): |
||||
|
l[i] = ('+', '-')[l[i] == '+'] |
||||
|
res.tzoffset = None |
||||
|
if info.utczone(res.tzname): |
||||
|
# With something like GMT+3, the timezone |
||||
|
# is *not* GMT. |
||||
|
res.tzname = None |
||||
|
|
||||
|
continue |
||||
|
|
||||
|
# Check for a numbered timezone |
||||
|
if res.hour is not None and l[i] in ('+', '-'): |
||||
|
signal = (-1,1)[l[i] == '+'] |
||||
|
i += 1 |
||||
|
len_li = len(l[i]) |
||||
|
if len_li == 4: |
||||
|
# -0300 |
||||
|
res.tzoffset = int(l[i][:2])*3600+int(l[i][2:])*60 |
||||
|
elif i+1 < len_l and l[i+1] == ':': |
||||
|
# -03:00 |
||||
|
res.tzoffset = int(l[i])*3600+int(l[i+2])*60 |
||||
|
i += 2 |
||||
|
elif len_li <= 2: |
||||
|
# -[0]3 |
||||
|
res.tzoffset = int(l[i][:2])*3600 |
||||
|
else: |
||||
|
return None |
||||
|
i += 1 |
||||
|
res.tzoffset *= signal |
||||
|
|
||||
|
# Look for a timezone name between parenthesis |
||||
|
if (i+3 < len_l and |
||||
|
info.jump(l[i]) and l[i+1] == '(' and l[i+3] == ')' and |
||||
|
3 <= len(l[i+2]) <= 5 and |
||||
|
not [x for x in l[i+2] |
||||
|
if x not in string.ascii_uppercase]): |
||||
|
# -0300 (BRST) |
||||
|
res.tzname = l[i+2] |
||||
|
i += 4 |
||||
|
continue |
||||
|
|
||||
|
# Check jumps |
||||
|
if not (info.jump(l[i]) or fuzzy): |
||||
|
return None |
||||
|
|
||||
|
i += 1 |
||||
|
|
||||
|
# Process year/month/day |
||||
|
len_ymd = len(ymd) |
||||
|
if len_ymd > 3: |
||||
|
# More than three members!? |
||||
|
return None |
||||
|
elif len_ymd == 1 or (mstridx != -1 and len_ymd == 2): |
||||
|
# One member, or two members with a month string |
||||
|
if mstridx != -1: |
||||
|
res.month = ymd[mstridx] |
||||
|
del ymd[mstridx] |
||||
|
if len_ymd > 1 or mstridx == -1: |
||||
|
if ymd[0] > 31: |
||||
|
res.year = ymd[0] |
||||
|
else: |
||||
|
res.day = ymd[0] |
||||
|
elif len_ymd == 2: |
||||
|
# Two members with numbers |
||||
|
if ymd[0] > 31: |
||||
|
# 99-01 |
||||
|
res.year, res.month = ymd |
||||
|
elif ymd[1] > 31: |
||||
|
# 01-99 |
||||
|
res.month, res.year = ymd |
||||
|
elif dayfirst and ymd[1] <= 12: |
||||
|
# 13-01 |
||||
|
res.day, res.month = ymd |
||||
|
else: |
||||
|
# 01-13 |
||||
|
res.month, res.day = ymd |
||||
|
if len_ymd == 3: |
||||
|
# Three members |
||||
|
if mstridx == 0: |
||||
|
res.month, res.day, res.year = ymd |
||||
|
elif mstridx == 1: |
||||
|
if ymd[0] > 31 or (yearfirst and ymd[2] <= 31): |
||||
|
# 99-Jan-01 |
||||
|
res.year, res.month, res.day = ymd |
||||
|
else: |
||||
|
# 01-Jan-01 |
||||
|
# Give precendence to day-first, since |
||||
|
# two-digit years is usually hand-written. |
||||
|
res.day, res.month, res.year = ymd |
||||
|
elif mstridx == 2: |
||||
|
# WTF!? |
||||
|
if ymd[1] > 31: |
||||
|
# 01-99-Jan |
||||
|
res.day, res.year, res.month = ymd |
||||
|
else: |
||||
|
# 99-01-Jan |
||||
|
res.year, res.day, res.month = ymd |
||||
|
else: |
||||
|
if ymd[0] > 31 or \ |
||||
|
(yearfirst and ymd[1] <= 12 and ymd[2] <= 31): |
||||
|
# 99-01-01 |
||||
|
res.year, res.month, res.day = ymd |
||||
|
elif ymd[0] > 12 or (dayfirst and ymd[1] <= 12): |
||||
|
# 13-01-01 |
||||
|
res.day, res.month, res.year = ymd |
||||
|
else: |
||||
|
# 01-13-01 |
||||
|
res.month, res.day, res.year = ymd |
||||
|
|
||||
|
except (IndexError, ValueError, AssertionError): |
||||
|
return None |
||||
|
|
||||
|
if not info.validate(res): |
||||
|
return None |
||||
|
return res |
||||
|
|
||||
|
DEFAULTPARSER = parser() |
||||
|
def parse(timestr, parserinfo=None, **kwargs): |
||||
|
if parserinfo: |
||||
|
return parser(parserinfo).parse(timestr, **kwargs) |
||||
|
else: |
||||
|
return DEFAULTPARSER.parse(timestr, **kwargs) |
||||
|
|
||||
|
|
||||
|
class _tzparser(object): |
||||
|
|
||||
|
class _result(_resultbase): |
||||
|
|
||||
|
__slots__ = ["stdabbr", "stdoffset", "dstabbr", "dstoffset", |
||||
|
"start", "end"] |
||||
|
|
||||
|
class _attr(_resultbase): |
||||
|
__slots__ = ["month", "week", "weekday", |
||||
|
"yday", "jyday", "day", "time"] |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return self._repr("") |
||||
|
|
||||
|
def __init__(self): |
||||
|
_resultbase.__init__(self) |
||||
|
self.start = self._attr() |
||||
|
self.end = self._attr() |
||||
|
|
||||
|
def parse(self, tzstr): |
||||
|
res = self._result() |
||||
|
l = _timelex.split(tzstr) |
||||
|
try: |
||||
|
|
||||
|
len_l = len(l) |
||||
|
|
||||
|
i = 0 |
||||
|
while i < len_l: |
||||
|
# BRST+3[BRDT[+2]] |
||||
|
j = i |
||||
|
while j < len_l and not [x for x in l[j] |
||||
|
if x in "0123456789:,-+"]: |
||||
|
j += 1 |
||||
|
if j != i: |
||||
|
if not res.stdabbr: |
||||
|
offattr = "stdoffset" |
||||
|
res.stdabbr = "".join(l[i:j]) |
||||
|
else: |
||||
|
offattr = "dstoffset" |
||||
|
res.dstabbr = "".join(l[i:j]) |
||||
|
i = j |
||||
|
if (i < len_l and |
||||
|
(l[i] in ('+', '-') or l[i][0] in "0123456789")): |
||||
|
if l[i] in ('+', '-'): |
||||
|
# Yes, that's right. See the TZ variable |
||||
|
# documentation. |
||||
|
signal = (1,-1)[l[i] == '+'] |
||||
|
i += 1 |
||||
|
else: |
||||
|
signal = -1 |
||||
|
len_li = len(l[i]) |
||||
|
if len_li == 4: |
||||
|
# -0300 |
||||
|
setattr(res, offattr, |
||||
|
(int(l[i][:2])*3600+int(l[i][2:])*60)*signal) |
||||
|
elif i+1 < len_l and l[i+1] == ':': |
||||
|
# -03:00 |
||||
|
setattr(res, offattr, |
||||
|
(int(l[i])*3600+int(l[i+2])*60)*signal) |
||||
|
i += 2 |
||||
|
elif len_li <= 2: |
||||
|
# -[0]3 |
||||
|
setattr(res, offattr, |
||||
|
int(l[i][:2])*3600*signal) |
||||
|
else: |
||||
|
return None |
||||
|
i += 1 |
||||
|
if res.dstabbr: |
||||
|
break |
||||
|
else: |
||||
|
break |
||||
|
|
||||
|
if i < len_l: |
||||
|
for j in range(i, len_l): |
||||
|
if l[j] == ';': l[j] = ',' |
||||
|
|
||||
|
assert l[i] == ',' |
||||
|
|
||||
|
i += 1 |
||||
|
|
||||
|
if i >= len_l: |
||||
|
pass |
||||
|
elif (8 <= l.count(',') <= 9 and |
||||
|
not [y for x in l[i:] if x != ',' |
||||
|
for y in x if y not in "0123456789"]): |
||||
|
# GMT0BST,3,0,30,3600,10,0,26,7200[,3600] |
||||
|
for x in (res.start, res.end): |
||||
|
x.month = int(l[i]) |
||||
|
i += 2 |
||||
|
if l[i] == '-': |
||||
|
value = int(l[i+1])*-1 |
||||
|
i += 1 |
||||
|
else: |
||||
|
value = int(l[i]) |
||||
|
i += 2 |
||||
|
if value: |
||||
|
x.week = value |
||||
|
x.weekday = (int(l[i])-1)%7 |
||||
|
else: |
||||
|
x.day = int(l[i]) |
||||
|
i += 2 |
||||
|
x.time = int(l[i]) |
||||
|
i += 2 |
||||
|
if i < len_l: |
||||
|
if l[i] in ('-','+'): |
||||
|
signal = (-1,1)[l[i] == "+"] |
||||
|
i += 1 |
||||
|
else: |
||||
|
signal = 1 |
||||
|
res.dstoffset = (res.stdoffset+int(l[i]))*signal |
||||
|
elif (l.count(',') == 2 and l[i:].count('/') <= 2 and |
||||
|
not [y for x in l[i:] if x not in (',','/','J','M', |
||||
|
'.','-',':') |
||||
|
for y in x if y not in "0123456789"]): |
||||
|
for x in (res.start, res.end): |
||||
|
if l[i] == 'J': |
||||
|
# non-leap year day (1 based) |
||||
|
i += 1 |
||||
|
x.jyday = int(l[i]) |
||||
|
elif l[i] == 'M': |
||||
|
# month[-.]week[-.]weekday |
||||
|
i += 1 |
||||
|
x.month = int(l[i]) |
||||
|
i += 1 |
||||
|
assert l[i] in ('-', '.') |
||||
|
i += 1 |
||||
|
x.week = int(l[i]) |
||||
|
if x.week == 5: |
||||
|
x.week = -1 |
||||
|
i += 1 |
||||
|
assert l[i] in ('-', '.') |
||||
|
i += 1 |
||||
|
x.weekday = (int(l[i])-1)%7 |
||||
|
else: |
||||
|
# year day (zero based) |
||||
|
x.yday = int(l[i])+1 |
||||
|
|
||||
|
i += 1 |
||||
|
|
||||
|
if i < len_l and l[i] == '/': |
||||
|
i += 1 |
||||
|
# start time |
||||
|
len_li = len(l[i]) |
||||
|
if len_li == 4: |
||||
|
# -0300 |
||||
|
x.time = (int(l[i][:2])*3600+int(l[i][2:])*60) |
||||
|
elif i+1 < len_l and l[i+1] == ':': |
||||
|
# -03:00 |
||||
|
x.time = int(l[i])*3600+int(l[i+2])*60 |
||||
|
i += 2 |
||||
|
if i+1 < len_l and l[i+1] == ':': |
||||
|
i += 2 |
||||
|
x.time += int(l[i]) |
||||
|
elif len_li <= 2: |
||||
|
# -[0]3 |
||||
|
x.time = (int(l[i][:2])*3600) |
||||
|
else: |
||||
|
return None |
||||
|
i += 1 |
||||
|
|
||||
|
assert i == len_l or l[i] == ',' |
||||
|
|
||||
|
i += 1 |
||||
|
|
||||
|
assert i >= len_l |
||||
|
|
||||
|
except (IndexError, ValueError, AssertionError): |
||||
|
return None |
||||
|
|
||||
|
return res |
||||
|
|
||||
|
|
||||
|
DEFAULTTZPARSER = _tzparser() |
||||
|
def _parsetz(tzstr): |
||||
|
return DEFAULTTZPARSER.parse(tzstr) |
||||
|
|
||||
|
|
||||
|
def _parsems(value): |
||||
|
"""Parse a I[.F] seconds value into (seconds, microseconds).""" |
||||
|
if "." not in value: |
||||
|
return int(value), 0 |
||||
|
else: |
||||
|
i, f = value.split(".") |
||||
|
return int(i), int(f.ljust(6, "0")[:6]) |
||||
|
|
||||
|
|
||||
|
# vim:ts=4:sw=4:et |
@ -0,0 +1,432 @@ |
|||||
|
""" |
||||
|
Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net> |
||||
|
|
||||
|
This module offers extensions to the standard python 2.3+ |
||||
|
datetime module. |
||||
|
""" |
||||
|
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" |
||||
|
__license__ = "PSF License" |
||||
|
|
||||
|
import datetime |
||||
|
import calendar |
||||
|
|
||||
|
__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"] |
||||
|
|
||||
|
class weekday(object): |
||||
|
__slots__ = ["weekday", "n"] |
||||
|
|
||||
|
def __init__(self, weekday, n=None): |
||||
|
self.weekday = weekday |
||||
|
self.n = n |
||||
|
|
||||
|
def __call__(self, n): |
||||
|
if n == self.n: |
||||
|
return self |
||||
|
else: |
||||
|
return self.__class__(self.weekday, n) |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
try: |
||||
|
if self.weekday != other.weekday or self.n != other.n: |
||||
|
return False |
||||
|
except AttributeError: |
||||
|
return False |
||||
|
return True |
||||
|
|
||||
|
def __repr__(self): |
||||
|
s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday] |
||||
|
if not self.n: |
||||
|
return s |
||||
|
else: |
||||
|
return "%s(%+d)" % (s, self.n) |
||||
|
|
||||
|
MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)]) |
||||
|
|
||||
|
class relativedelta: |
||||
|
""" |
||||
|
The relativedelta type is based on the specification of the excelent |
||||
|
work done by M.-A. Lemburg in his mx.DateTime extension. However, |
||||
|
notice that this type does *NOT* implement the same algorithm as |
||||
|
his work. Do *NOT* expect it to behave like mx.DateTime's counterpart. |
||||
|
|
||||
|
There's two different ways to build a relativedelta instance. The |
||||
|
first one is passing it two date/datetime classes: |
||||
|
|
||||
|
relativedelta(datetime1, datetime2) |
||||
|
|
||||
|
And the other way is to use the following keyword arguments: |
||||
|
|
||||
|
year, month, day, hour, minute, second, microsecond: |
||||
|
Absolute information. |
||||
|
|
||||
|
years, months, weeks, days, hours, minutes, seconds, microseconds: |
||||
|
Relative information, may be negative. |
||||
|
|
||||
|
weekday: |
||||
|
One of the weekday instances (MO, TU, etc). These instances may |
||||
|
receive a parameter N, specifying the Nth weekday, which could |
||||
|
be positive or negative (like MO(+1) or MO(-2). Not specifying |
||||
|
it is the same as specifying +1. You can also use an integer, |
||||
|
where 0=MO. |
||||
|
|
||||
|
leapdays: |
||||
|
Will add given days to the date found, if year is a leap |
||||
|
year, and the date found is post 28 of february. |
||||
|
|
||||
|
yearday, nlyearday: |
||||
|
Set the yearday or the non-leap year day (jump leap days). |
||||
|
These are converted to day/month/leapdays information. |
||||
|
|
||||
|
Here is the behavior of operations with relativedelta: |
||||
|
|
||||
|
1) Calculate the absolute year, using the 'year' argument, or the |
||||
|
original datetime year, if the argument is not present. |
||||
|
|
||||
|
2) Add the relative 'years' argument to the absolute year. |
||||
|
|
||||
|
3) Do steps 1 and 2 for month/months. |
||||
|
|
||||
|
4) Calculate the absolute day, using the 'day' argument, or the |
||||
|
original datetime day, if the argument is not present. Then, |
||||
|
subtract from the day until it fits in the year and month |
||||
|
found after their operations. |
||||
|
|
||||
|
5) Add the relative 'days' argument to the absolute day. Notice |
||||
|
that the 'weeks' argument is multiplied by 7 and added to |
||||
|
'days'. |
||||
|
|
||||
|
6) Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds, |
||||
|
microsecond/microseconds. |
||||
|
|
||||
|
7) If the 'weekday' argument is present, calculate the weekday, |
||||
|
with the given (wday, nth) tuple. wday is the index of the |
||||
|
weekday (0-6, 0=Mon), and nth is the number of weeks to add |
||||
|
forward or backward, depending on its signal. Notice that if |
||||
|
the calculated date is already Monday, for example, using |
||||
|
(0, 1) or (0, -1) won't change the day. |
||||
|
""" |
||||
|
|
||||
|
def __init__(self, dt1=None, dt2=None, |
||||
|
years=0, months=0, days=0, leapdays=0, weeks=0, |
||||
|
hours=0, minutes=0, seconds=0, microseconds=0, |
||||
|
year=None, month=None, day=None, weekday=None, |
||||
|
yearday=None, nlyearday=None, |
||||
|
hour=None, minute=None, second=None, microsecond=None): |
||||
|
if dt1 and dt2: |
||||
|
if not isinstance(dt1, datetime.date) or \ |
||||
|
not isinstance(dt2, datetime.date): |
||||
|
raise TypeError, "relativedelta only diffs datetime/date" |
||||
|
if type(dt1) is not type(dt2): |
||||
|
if not isinstance(dt1, datetime.datetime): |
||||
|
dt1 = datetime.datetime.fromordinal(dt1.toordinal()) |
||||
|
elif not isinstance(dt2, datetime.datetime): |
||||
|
dt2 = datetime.datetime.fromordinal(dt2.toordinal()) |
||||
|
self.years = 0 |
||||
|
self.months = 0 |
||||
|
self.days = 0 |
||||
|
self.leapdays = 0 |
||||
|
self.hours = 0 |
||||
|
self.minutes = 0 |
||||
|
self.seconds = 0 |
||||
|
self.microseconds = 0 |
||||
|
self.year = None |
||||
|
self.month = None |
||||
|
self.day = None |
||||
|
self.weekday = None |
||||
|
self.hour = None |
||||
|
self.minute = None |
||||
|
self.second = None |
||||
|
self.microsecond = None |
||||
|
self._has_time = 0 |
||||
|
|
||||
|
months = (dt1.year*12+dt1.month)-(dt2.year*12+dt2.month) |
||||
|
self._set_months(months) |
||||
|
dtm = self.__radd__(dt2) |
||||
|
if dt1 < dt2: |
||||
|
while dt1 > dtm: |
||||
|
months += 1 |
||||
|
self._set_months(months) |
||||
|
dtm = self.__radd__(dt2) |
||||
|
else: |
||||
|
while dt1 < dtm: |
||||
|
months -= 1 |
||||
|
self._set_months(months) |
||||
|
dtm = self.__radd__(dt2) |
||||
|
delta = dt1 - dtm |
||||
|
self.seconds = delta.seconds+delta.days*86400 |
||||
|
self.microseconds = delta.microseconds |
||||
|
else: |
||||
|
self.years = years |
||||
|
self.months = months |
||||
|
self.days = days+weeks*7 |
||||
|
self.leapdays = leapdays |
||||
|
self.hours = hours |
||||
|
self.minutes = minutes |
||||
|
self.seconds = seconds |
||||
|
self.microseconds = microseconds |
||||
|
self.year = year |
||||
|
self.month = month |
||||
|
self.day = day |
||||
|
self.hour = hour |
||||
|
self.minute = minute |
||||
|
self.second = second |
||||
|
self.microsecond = microsecond |
||||
|
|
||||
|
if type(weekday) is int: |
||||
|
self.weekday = weekdays[weekday] |
||||
|
else: |
||||
|
self.weekday = weekday |
||||
|
|
||||
|
yday = 0 |
||||
|
if nlyearday: |
||||
|
yday = nlyearday |
||||
|
elif yearday: |
||||
|
yday = yearday |
||||
|
if yearday > 59: |
||||
|
self.leapdays = -1 |
||||
|
if yday: |
||||
|
ydayidx = [31,59,90,120,151,181,212,243,273,304,334,366] |
||||
|
for idx, ydays in enumerate(ydayidx): |
||||
|
if yday <= ydays: |
||||
|
self.month = idx+1 |
||||
|
if idx == 0: |
||||
|
self.day = yday |
||||
|
else: |
||||
|
self.day = yday-ydayidx[idx-1] |
||||
|
break |
||||
|
else: |
||||
|
raise ValueError, "invalid year day (%d)" % yday |
||||
|
|
||||
|
self._fix() |
||||
|
|
||||
|
def _fix(self): |
||||
|
if abs(self.microseconds) > 999999: |
||||
|
s = self.microseconds//abs(self.microseconds) |
||||
|
div, mod = divmod(self.microseconds*s, 1000000) |
||||
|
self.microseconds = mod*s |
||||
|
self.seconds += div*s |
||||
|
if abs(self.seconds) > 59: |
||||
|
s = self.seconds//abs(self.seconds) |
||||
|
div, mod = divmod(self.seconds*s, 60) |
||||
|
self.seconds = mod*s |
||||
|
self.minutes += div*s |
||||
|
if abs(self.minutes) > 59: |
||||
|
s = self.minutes//abs(self.minutes) |
||||
|
div, mod = divmod(self.minutes*s, 60) |
||||
|
self.minutes = mod*s |
||||
|
self.hours += div*s |
||||
|
if abs(self.hours) > 23: |
||||
|
s = self.hours//abs(self.hours) |
||||
|
div, mod = divmod(self.hours*s, 24) |
||||
|
self.hours = mod*s |
||||
|
self.days += div*s |
||||
|
if abs(self.months) > 11: |
||||
|
s = self.months//abs(self.months) |
||||
|
div, mod = divmod(self.months*s, 12) |
||||
|
self.months = mod*s |
||||
|
self.years += div*s |
||||
|
if (self.hours or self.minutes or self.seconds or self.microseconds or |
||||
|
self.hour is not None or self.minute is not None or |
||||
|
self.second is not None or self.microsecond is not None): |
||||
|
self._has_time = 1 |
||||
|
else: |
||||
|
self._has_time = 0 |
||||
|
|
||||
|
def _set_months(self, months): |
||||
|
self.months = months |
||||
|
if abs(self.months) > 11: |
||||
|
s = self.months//abs(self.months) |
||||
|
div, mod = divmod(self.months*s, 12) |
||||
|
self.months = mod*s |
||||
|
self.years = div*s |
||||
|
else: |
||||
|
self.years = 0 |
||||
|
|
||||
|
def __radd__(self, other): |
||||
|
if not isinstance(other, datetime.date): |
||||
|
raise TypeError, "unsupported type for add operation" |
||||
|
elif self._has_time and not isinstance(other, datetime.datetime): |
||||
|
other = datetime.datetime.fromordinal(other.toordinal()) |
||||
|
year = (self.year or other.year)+self.years |
||||
|
month = self.month or other.month |
||||
|
if self.months: |
||||
|
assert 1 <= abs(self.months) <= 12 |
||||
|
month += self.months |
||||
|
if month > 12: |
||||
|
year += 1 |
||||
|
month -= 12 |
||||
|
elif month < 1: |
||||
|
year -= 1 |
||||
|
month += 12 |
||||
|
day = min(calendar.monthrange(year, month)[1], |
||||
|
self.day or other.day) |
||||
|
repl = {"year": year, "month": month, "day": day} |
||||
|
for attr in ["hour", "minute", "second", "microsecond"]: |
||||
|
value = getattr(self, attr) |
||||
|
if value is not None: |
||||
|
repl[attr] = value |
||||
|
days = self.days |
||||
|
if self.leapdays and month > 2 and calendar.isleap(year): |
||||
|
days += self.leapdays |
||||
|
ret = (other.replace(**repl) |
||||
|
+ datetime.timedelta(days=days, |
||||
|
hours=self.hours, |
||||
|
minutes=self.minutes, |
||||
|
seconds=self.seconds, |
||||
|
microseconds=self.microseconds)) |
||||
|
if self.weekday: |
||||
|
weekday, nth = self.weekday.weekday, self.weekday.n or 1 |
||||
|
jumpdays = (abs(nth)-1)*7 |
||||
|
if nth > 0: |
||||
|
jumpdays += (7-ret.weekday()+weekday)%7 |
||||
|
else: |
||||
|
jumpdays += (ret.weekday()-weekday)%7 |
||||
|
jumpdays *= -1 |
||||
|
ret += datetime.timedelta(days=jumpdays) |
||||
|
return ret |
||||
|
|
||||
|
def __rsub__(self, other): |
||||
|
return self.__neg__().__radd__(other) |
||||
|
|
||||
|
def __add__(self, other): |
||||
|
if not isinstance(other, relativedelta): |
||||
|
raise TypeError, "unsupported type for add operation" |
||||
|
return relativedelta(years=other.years+self.years, |
||||
|
months=other.months+self.months, |
||||
|
days=other.days+self.days, |
||||
|
hours=other.hours+self.hours, |
||||
|
minutes=other.minutes+self.minutes, |
||||
|
seconds=other.seconds+self.seconds, |
||||
|
microseconds=other.microseconds+self.microseconds, |
||||
|
leapdays=other.leapdays or self.leapdays, |
||||
|
year=other.year or self.year, |
||||
|
month=other.month or self.month, |
||||
|
day=other.day or self.day, |
||||
|
weekday=other.weekday or self.weekday, |
||||
|
hour=other.hour or self.hour, |
||||
|
minute=other.minute or self.minute, |
||||
|
second=other.second or self.second, |
||||
|
microsecond=other.second or self.microsecond) |
||||
|
|
||||
|
def __sub__(self, other): |
||||
|
if not isinstance(other, relativedelta): |
||||
|
raise TypeError, "unsupported type for sub operation" |
||||
|
return relativedelta(years=other.years-self.years, |
||||
|
months=other.months-self.months, |
||||
|
days=other.days-self.days, |
||||
|
hours=other.hours-self.hours, |
||||
|
minutes=other.minutes-self.minutes, |
||||
|
seconds=other.seconds-self.seconds, |
||||
|
microseconds=other.microseconds-self.microseconds, |
||||
|
leapdays=other.leapdays or self.leapdays, |
||||
|
year=other.year or self.year, |
||||
|
month=other.month or self.month, |
||||
|
day=other.day or self.day, |
||||
|
weekday=other.weekday or self.weekday, |
||||
|
hour=other.hour or self.hour, |
||||
|
minute=other.minute or self.minute, |
||||
|
second=other.second or self.second, |
||||
|
microsecond=other.second or self.microsecond) |
||||
|
|
||||
|
def __neg__(self): |
||||
|
return relativedelta(years=-self.years, |
||||
|
months=-self.months, |
||||
|
days=-self.days, |
||||
|
hours=-self.hours, |
||||
|
minutes=-self.minutes, |
||||
|
seconds=-self.seconds, |
||||
|
microseconds=-self.microseconds, |
||||
|
leapdays=self.leapdays, |
||||
|
year=self.year, |
||||
|
month=self.month, |
||||
|
day=self.day, |
||||
|
weekday=self.weekday, |
||||
|
hour=self.hour, |
||||
|
minute=self.minute, |
||||
|
second=self.second, |
||||
|
microsecond=self.microsecond) |
||||
|
|
||||
|
def __nonzero__(self): |
||||
|
return not (not self.years and |
||||
|
not self.months and |
||||
|
not self.days and |
||||
|
not self.hours and |
||||
|
not self.minutes and |
||||
|
not self.seconds and |
||||
|
not self.microseconds and |
||||
|
not self.leapdays and |
||||
|
self.year is None and |
||||
|
self.month is None and |
||||
|
self.day is None and |
||||
|
self.weekday is None and |
||||
|
self.hour is None and |
||||
|
self.minute is None and |
||||
|
self.second is None and |
||||
|
self.microsecond is None) |
||||
|
|
||||
|
def __mul__(self, other): |
||||
|
f = float(other) |
||||
|
return relativedelta(years=self.years*f, |
||||
|
months=self.months*f, |
||||
|
days=self.days*f, |
||||
|
hours=self.hours*f, |
||||
|
minutes=self.minutes*f, |
||||
|
seconds=self.seconds*f, |
||||
|
microseconds=self.microseconds*f, |
||||
|
leapdays=self.leapdays, |
||||
|
year=self.year, |
||||
|
month=self.month, |
||||
|
day=self.day, |
||||
|
weekday=self.weekday, |
||||
|
hour=self.hour, |
||||
|
minute=self.minute, |
||||
|
second=self.second, |
||||
|
microsecond=self.microsecond) |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
if not isinstance(other, relativedelta): |
||||
|
return False |
||||
|
if self.weekday or other.weekday: |
||||
|
if not self.weekday or not other.weekday: |
||||
|
return False |
||||
|
if self.weekday.weekday != other.weekday.weekday: |
||||
|
return False |
||||
|
n1, n2 = self.weekday.n, other.weekday.n |
||||
|
if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)): |
||||
|
return False |
||||
|
return (self.years == other.years and |
||||
|
self.months == other.months and |
||||
|
self.days == other.days and |
||||
|
self.hours == other.hours and |
||||
|
self.minutes == other.minutes and |
||||
|
self.seconds == other.seconds and |
||||
|
self.leapdays == other.leapdays and |
||||
|
self.year == other.year and |
||||
|
self.month == other.month and |
||||
|
self.day == other.day and |
||||
|
self.hour == other.hour and |
||||
|
self.minute == other.minute and |
||||
|
self.second == other.second and |
||||
|
self.microsecond == other.microsecond) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __div__(self, other): |
||||
|
return self.__mul__(1/float(other)) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
l = [] |
||||
|
for attr in ["years", "months", "days", "leapdays", |
||||
|
"hours", "minutes", "seconds", "microseconds"]: |
||||
|
value = getattr(self, attr) |
||||
|
if value: |
||||
|
l.append("%s=%+d" % (attr, value)) |
||||
|
for attr in ["year", "month", "day", "weekday", |
||||
|
"hour", "minute", "second", "microsecond"]: |
||||
|
value = getattr(self, attr) |
||||
|
if value is not None: |
||||
|
l.append("%s=%s" % (attr, `value`)) |
||||
|
return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) |
||||
|
|
||||
|
# vim:ts=4:sw=4:et |
File diff suppressed because it is too large
@ -0,0 +1,951 @@ |
|||||
|
""" |
||||
|
Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net> |
||||
|
|
||||
|
This module offers extensions to the standard python 2.3+ |
||||
|
datetime module. |
||||
|
""" |
||||
|
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" |
||||
|
__license__ = "PSF License" |
||||
|
|
||||
|
import datetime |
||||
|
import struct |
||||
|
import time |
||||
|
import sys |
||||
|
import os |
||||
|
|
||||
|
relativedelta = None |
||||
|
parser = None |
||||
|
rrule = None |
||||
|
|
||||
|
__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange", |
||||
|
"tzstr", "tzical", "tzwin", "tzwinlocal", "gettz"] |
||||
|
|
||||
|
try: |
||||
|
from dateutil.tzwin import tzwin, tzwinlocal |
||||
|
except (ImportError, OSError): |
||||
|
tzwin, tzwinlocal = None, None |
||||
|
|
||||
|
ZERO = datetime.timedelta(0) |
||||
|
EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal() |
||||
|
|
||||
|
class tzutc(datetime.tzinfo): |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
return ZERO |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
return ZERO |
||||
|
|
||||
|
def tzname(self, dt): |
||||
|
return "UTC" |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
return (isinstance(other, tzutc) or |
||||
|
(isinstance(other, tzoffset) and other._offset == ZERO)) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s()" % self.__class__.__name__ |
||||
|
|
||||
|
__reduce__ = object.__reduce__ |
||||
|
|
||||
|
class tzoffset(datetime.tzinfo): |
||||
|
|
||||
|
def __init__(self, name, offset): |
||||
|
self._name = name |
||||
|
self._offset = datetime.timedelta(seconds=offset) |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
return self._offset |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
return ZERO |
||||
|
|
||||
|
def tzname(self, dt): |
||||
|
return self._name |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
return (isinstance(other, tzoffset) and |
||||
|
self._offset == other._offset) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s(%s, %s)" % (self.__class__.__name__, |
||||
|
`self._name`, |
||||
|
self._offset.days*86400+self._offset.seconds) |
||||
|
|
||||
|
__reduce__ = object.__reduce__ |
||||
|
|
||||
|
class tzlocal(datetime.tzinfo): |
||||
|
|
||||
|
_std_offset = datetime.timedelta(seconds=-time.timezone) |
||||
|
if time.daylight: |
||||
|
_dst_offset = datetime.timedelta(seconds=-time.altzone) |
||||
|
else: |
||||
|
_dst_offset = _std_offset |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return self._dst_offset |
||||
|
else: |
||||
|
return self._std_offset |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return self._dst_offset-self._std_offset |
||||
|
else: |
||||
|
return ZERO |
||||
|
|
||||
|
def tzname(self, dt): |
||||
|
return time.tzname[self._isdst(dt)] |
||||
|
|
||||
|
def _isdst(self, dt): |
||||
|
# We can't use mktime here. It is unstable when deciding if |
||||
|
# the hour near to a change is DST or not. |
||||
|
# |
||||
|
# timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour, |
||||
|
# dt.minute, dt.second, dt.weekday(), 0, -1)) |
||||
|
# return time.localtime(timestamp).tm_isdst |
||||
|
# |
||||
|
# The code above yields the following result: |
||||
|
# |
||||
|
#>>> import tz, datetime |
||||
|
#>>> t = tz.tzlocal() |
||||
|
#>>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() |
||||
|
#'BRDT' |
||||
|
#>>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname() |
||||
|
#'BRST' |
||||
|
#>>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() |
||||
|
#'BRST' |
||||
|
#>>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname() |
||||
|
#'BRDT' |
||||
|
#>>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname() |
||||
|
#'BRDT' |
||||
|
# |
||||
|
# Here is a more stable implementation: |
||||
|
# |
||||
|
timestamp = ((dt.toordinal() - EPOCHORDINAL) * 86400 |
||||
|
+ dt.hour * 3600 |
||||
|
+ dt.minute * 60 |
||||
|
+ dt.second) |
||||
|
return time.localtime(timestamp+time.timezone).tm_isdst |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
if not isinstance(other, tzlocal): |
||||
|
return False |
||||
|
return (self._std_offset == other._std_offset and |
||||
|
self._dst_offset == other._dst_offset) |
||||
|
return True |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s()" % self.__class__.__name__ |
||||
|
|
||||
|
__reduce__ = object.__reduce__ |
||||
|
|
||||
|
class _ttinfo(object): |
||||
|
__slots__ = ["offset", "delta", "isdst", "abbr", "isstd", "isgmt"] |
||||
|
|
||||
|
def __init__(self): |
||||
|
for attr in self.__slots__: |
||||
|
setattr(self, attr, None) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
l = [] |
||||
|
for attr in self.__slots__: |
||||
|
value = getattr(self, attr) |
||||
|
if value is not None: |
||||
|
l.append("%s=%s" % (attr, `value`)) |
||||
|
return "%s(%s)" % (self.__class__.__name__, ", ".join(l)) |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
if not isinstance(other, _ttinfo): |
||||
|
return False |
||||
|
return (self.offset == other.offset and |
||||
|
self.delta == other.delta and |
||||
|
self.isdst == other.isdst and |
||||
|
self.abbr == other.abbr and |
||||
|
self.isstd == other.isstd and |
||||
|
self.isgmt == other.isgmt) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __getstate__(self): |
||||
|
state = {} |
||||
|
for name in self.__slots__: |
||||
|
state[name] = getattr(self, name, None) |
||||
|
return state |
||||
|
|
||||
|
def __setstate__(self, state): |
||||
|
for name in self.__slots__: |
||||
|
if name in state: |
||||
|
setattr(self, name, state[name]) |
||||
|
|
||||
|
class tzfile(datetime.tzinfo): |
||||
|
|
||||
|
# http://www.twinsun.com/tz/tz-link.htm |
||||
|
# ftp://elsie.nci.nih.gov/pub/tz*.tar.gz |
||||
|
|
||||
|
def __init__(self, fileobj): |
||||
|
if isinstance(fileobj, basestring): |
||||
|
self._filename = fileobj |
||||
|
fileobj = open(fileobj) |
||||
|
elif hasattr(fileobj, "name"): |
||||
|
self._filename = fileobj.name |
||||
|
else: |
||||
|
self._filename = `fileobj` |
||||
|
|
||||
|
# From tzfile(5): |
||||
|
# |
||||
|
# The time zone information files used by tzset(3) |
||||
|
# begin with the magic characters "TZif" to identify |
||||
|
# them as time zone information files, followed by |
||||
|
# sixteen bytes reserved for future use, followed by |
||||
|
# six four-byte values of type long, written in a |
||||
|
# ``standard'' byte order (the high-order byte |
||||
|
# of the value is written first). |
||||
|
|
||||
|
if fileobj.read(4) != "TZif": |
||||
|
raise ValueError, "magic not found" |
||||
|
|
||||
|
fileobj.read(16) |
||||
|
|
||||
|
( |
||||
|
# The number of UTC/local indicators stored in the file. |
||||
|
ttisgmtcnt, |
||||
|
|
||||
|
# The number of standard/wall indicators stored in the file. |
||||
|
ttisstdcnt, |
||||
|
|
||||
|
# The number of leap seconds for which data is |
||||
|
# stored in the file. |
||||
|
leapcnt, |
||||
|
|
||||
|
# The number of "transition times" for which data |
||||
|
# is stored in the file. |
||||
|
timecnt, |
||||
|
|
||||
|
# The number of "local time types" for which data |
||||
|
# is stored in the file (must not be zero). |
||||
|
typecnt, |
||||
|
|
||||
|
# The number of characters of "time zone |
||||
|
# abbreviation strings" stored in the file. |
||||
|
charcnt, |
||||
|
|
||||
|
) = struct.unpack(">6l", fileobj.read(24)) |
||||
|
|
||||
|
# The above header is followed by tzh_timecnt four-byte |
||||
|
# values of type long, sorted in ascending order. |
||||
|
# These values are written in ``standard'' byte order. |
||||
|
# Each is used as a transition time (as returned by |
||||
|
# time(2)) at which the rules for computing local time |
||||
|
# change. |
||||
|
|
||||
|
if timecnt: |
||||
|
self._trans_list = struct.unpack(">%dl" % timecnt, |
||||
|
fileobj.read(timecnt*4)) |
||||
|
else: |
||||
|
self._trans_list = [] |
||||
|
|
||||
|
# Next come tzh_timecnt one-byte values of type unsigned |
||||
|
# char; each one tells which of the different types of |
||||
|
# ``local time'' types described in the file is associated |
||||
|
# with the same-indexed transition time. These values |
||||
|
# serve as indices into an array of ttinfo structures that |
||||
|
# appears next in the file. |
||||
|
|
||||
|
if timecnt: |
||||
|
self._trans_idx = struct.unpack(">%dB" % timecnt, |
||||
|
fileobj.read(timecnt)) |
||||
|
else: |
||||
|
self._trans_idx = [] |
||||
|
|
||||
|
# Each ttinfo structure is written as a four-byte value |
||||
|
# for tt_gmtoff of type long, in a standard byte |
||||
|
# order, followed by a one-byte value for tt_isdst |
||||
|
# and a one-byte value for tt_abbrind. In each |
||||
|
# structure, tt_gmtoff gives the number of |
||||
|
# seconds to be added to UTC, tt_isdst tells whether |
||||
|
# tm_isdst should be set by localtime(3), and |
||||
|
# tt_abbrind serves as an index into the array of |
||||
|
# time zone abbreviation characters that follow the |
||||
|
# ttinfo structure(s) in the file. |
||||
|
|
||||
|
ttinfo = [] |
||||
|
|
||||
|
for i in range(typecnt): |
||||
|
ttinfo.append(struct.unpack(">lbb", fileobj.read(6))) |
||||
|
|
||||
|
abbr = fileobj.read(charcnt) |
||||
|
|
||||
|
# Then there are tzh_leapcnt pairs of four-byte |
||||
|
# values, written in standard byte order; the |
||||
|
# first value of each pair gives the time (as |
||||
|
# returned by time(2)) at which a leap second |
||||
|
# occurs; the second gives the total number of |
||||
|
# leap seconds to be applied after the given time. |
||||
|
# The pairs of values are sorted in ascending order |
||||
|
# by time. |
||||
|
|
||||
|
# Not used, for now |
||||
|
if leapcnt: |
||||
|
leap = struct.unpack(">%dl" % (leapcnt*2), |
||||
|
fileobj.read(leapcnt*8)) |
||||
|
|
||||
|
# Then there are tzh_ttisstdcnt standard/wall |
||||
|
# indicators, each stored as a one-byte value; |
||||
|
# they tell whether the transition times associated |
||||
|
# with local time types were specified as standard |
||||
|
# time or wall clock time, and are used when |
||||
|
# a time zone file is used in handling POSIX-style |
||||
|
# time zone environment variables. |
||||
|
|
||||
|
if ttisstdcnt: |
||||
|
isstd = struct.unpack(">%db" % ttisstdcnt, |
||||
|
fileobj.read(ttisstdcnt)) |
||||
|
|
||||
|
# Finally, there are tzh_ttisgmtcnt UTC/local |
||||
|
# indicators, each stored as a one-byte value; |
||||
|
# they tell whether the transition times associated |
||||
|
# with local time types were specified as UTC or |
||||
|
# local time, and are used when a time zone file |
||||
|
# is used in handling POSIX-style time zone envi- |
||||
|
# ronment variables. |
||||
|
|
||||
|
if ttisgmtcnt: |
||||
|
isgmt = struct.unpack(">%db" % ttisgmtcnt, |
||||
|
fileobj.read(ttisgmtcnt)) |
||||
|
|
||||
|
# ** Everything has been read ** |
||||
|
|
||||
|
# Build ttinfo list |
||||
|
self._ttinfo_list = [] |
||||
|
for i in range(typecnt): |
||||
|
gmtoff, isdst, abbrind = ttinfo[i] |
||||
|
# Round to full-minutes if that's not the case. Python's |
||||
|
# datetime doesn't accept sub-minute timezones. Check |
||||
|
# http://python.org/sf/1447945 for some information. |
||||
|
gmtoff = (gmtoff+30)//60*60 |
||||
|
tti = _ttinfo() |
||||
|
tti.offset = gmtoff |
||||
|
tti.delta = datetime.timedelta(seconds=gmtoff) |
||||
|
tti.isdst = isdst |
||||
|
tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)] |
||||
|
tti.isstd = (ttisstdcnt > i and isstd[i] != 0) |
||||
|
tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) |
||||
|
self._ttinfo_list.append(tti) |
||||
|
|
||||
|
# Replace ttinfo indexes for ttinfo objects. |
||||
|
trans_idx = [] |
||||
|
for idx in self._trans_idx: |
||||
|
trans_idx.append(self._ttinfo_list[idx]) |
||||
|
self._trans_idx = tuple(trans_idx) |
||||
|
|
||||
|
# Set standard, dst, and before ttinfos. before will be |
||||
|
# used when a given time is before any transitions, |
||||
|
# and will be set to the first non-dst ttinfo, or to |
||||
|
# the first dst, if all of them are dst. |
||||
|
self._ttinfo_std = None |
||||
|
self._ttinfo_dst = None |
||||
|
self._ttinfo_before = None |
||||
|
if self._ttinfo_list: |
||||
|
if not self._trans_list: |
||||
|
self._ttinfo_std = self._ttinfo_first = self._ttinfo_list[0] |
||||
|
else: |
||||
|
for i in range(timecnt-1,-1,-1): |
||||
|
tti = self._trans_idx[i] |
||||
|
if not self._ttinfo_std and not tti.isdst: |
||||
|
self._ttinfo_std = tti |
||||
|
elif not self._ttinfo_dst and tti.isdst: |
||||
|
self._ttinfo_dst = tti |
||||
|
if self._ttinfo_std and self._ttinfo_dst: |
||||
|
break |
||||
|
else: |
||||
|
if self._ttinfo_dst and not self._ttinfo_std: |
||||
|
self._ttinfo_std = self._ttinfo_dst |
||||
|
|
||||
|
for tti in self._ttinfo_list: |
||||
|
if not tti.isdst: |
||||
|
self._ttinfo_before = tti |
||||
|
break |
||||
|
else: |
||||
|
self._ttinfo_before = self._ttinfo_list[0] |
||||
|
|
||||
|
# Now fix transition times to become relative to wall time. |
||||
|
# |
||||
|
# I'm not sure about this. In my tests, the tz source file |
||||
|
# is setup to wall time, and in the binary file isstd and |
||||
|
# isgmt are off, so it should be in wall time. OTOH, it's |
||||
|
# always in gmt time. Let me know if you have comments |
||||
|
# about this. |
||||
|
laststdoffset = 0 |
||||
|
self._trans_list = list(self._trans_list) |
||||
|
for i in range(len(self._trans_list)): |
||||
|
tti = self._trans_idx[i] |
||||
|
if not tti.isdst: |
||||
|
# This is std time. |
||||
|
self._trans_list[i] += tti.offset |
||||
|
laststdoffset = tti.offset |
||||
|
else: |
||||
|
# This is dst time. Convert to std. |
||||
|
self._trans_list[i] += laststdoffset |
||||
|
self._trans_list = tuple(self._trans_list) |
||||
|
|
||||
|
def _find_ttinfo(self, dt, laststd=0): |
||||
|
timestamp = ((dt.toordinal() - EPOCHORDINAL) * 86400 |
||||
|
+ dt.hour * 3600 |
||||
|
+ dt.minute * 60 |
||||
|
+ dt.second) |
||||
|
idx = 0 |
||||
|
for trans in self._trans_list: |
||||
|
if timestamp < trans: |
||||
|
break |
||||
|
idx += 1 |
||||
|
else: |
||||
|
return self._ttinfo_std |
||||
|
if idx == 0: |
||||
|
return self._ttinfo_before |
||||
|
if laststd: |
||||
|
while idx > 0: |
||||
|
tti = self._trans_idx[idx-1] |
||||
|
if not tti.isdst: |
||||
|
return tti |
||||
|
idx -= 1 |
||||
|
else: |
||||
|
return self._ttinfo_std |
||||
|
else: |
||||
|
return self._trans_idx[idx-1] |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
if not self._ttinfo_std: |
||||
|
return ZERO |
||||
|
return self._find_ttinfo(dt).delta |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
if not self._ttinfo_dst: |
||||
|
return ZERO |
||||
|
tti = self._find_ttinfo(dt) |
||||
|
if not tti.isdst: |
||||
|
return ZERO |
||||
|
|
||||
|
# The documentation says that utcoffset()-dst() must |
||||
|
# be constant for every dt. |
||||
|
return tti.delta-self._find_ttinfo(dt, laststd=1).delta |
||||
|
|
||||
|
# An alternative for that would be: |
||||
|
# |
||||
|
# return self._ttinfo_dst.offset-self._ttinfo_std.offset |
||||
|
# |
||||
|
# However, this class stores historical changes in the |
||||
|
# dst offset, so I belive that this wouldn't be the right |
||||
|
# way to implement this. |
||||
|
|
||||
|
def tzname(self, dt): |
||||
|
if not self._ttinfo_std: |
||||
|
return None |
||||
|
return self._find_ttinfo(dt).abbr |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
if not isinstance(other, tzfile): |
||||
|
return False |
||||
|
return (self._trans_list == other._trans_list and |
||||
|
self._trans_idx == other._trans_idx and |
||||
|
self._ttinfo_list == other._ttinfo_list) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s(%s)" % (self.__class__.__name__, `self._filename`) |
||||
|
|
||||
|
def __reduce__(self): |
||||
|
if not os.path.isfile(self._filename): |
||||
|
raise ValueError, "Unpickable %s class" % self.__class__.__name__ |
||||
|
return (self.__class__, (self._filename,)) |
||||
|
|
||||
|
class tzrange(datetime.tzinfo): |
||||
|
|
||||
|
def __init__(self, stdabbr, stdoffset=None, |
||||
|
dstabbr=None, dstoffset=None, |
||||
|
start=None, end=None): |
||||
|
global relativedelta |
||||
|
if not relativedelta: |
||||
|
from dateutil import relativedelta |
||||
|
self._std_abbr = stdabbr |
||||
|
self._dst_abbr = dstabbr |
||||
|
if stdoffset is not None: |
||||
|
self._std_offset = datetime.timedelta(seconds=stdoffset) |
||||
|
else: |
||||
|
self._std_offset = ZERO |
||||
|
if dstoffset is not None: |
||||
|
self._dst_offset = datetime.timedelta(seconds=dstoffset) |
||||
|
elif dstabbr and stdoffset is not None: |
||||
|
self._dst_offset = self._std_offset+datetime.timedelta(hours=+1) |
||||
|
else: |
||||
|
self._dst_offset = ZERO |
||||
|
if dstabbr and start is None: |
||||
|
self._start_delta = relativedelta.relativedelta( |
||||
|
hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) |
||||
|
else: |
||||
|
self._start_delta = start |
||||
|
if dstabbr and end is None: |
||||
|
self._end_delta = relativedelta.relativedelta( |
||||
|
hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) |
||||
|
else: |
||||
|
self._end_delta = end |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return self._dst_offset |
||||
|
else: |
||||
|
return self._std_offset |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return self._dst_offset-self._std_offset |
||||
|
else: |
||||
|
return ZERO |
||||
|
|
||||
|
def tzname(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return self._dst_abbr |
||||
|
else: |
||||
|
return self._std_abbr |
||||
|
|
||||
|
def _isdst(self, dt): |
||||
|
if not self._start_delta: |
||||
|
return False |
||||
|
year = datetime.datetime(dt.year,1,1) |
||||
|
start = year+self._start_delta |
||||
|
end = year+self._end_delta |
||||
|
dt = dt.replace(tzinfo=None) |
||||
|
if start < end: |
||||
|
return dt >= start and dt < end |
||||
|
else: |
||||
|
return dt >= start or dt < end |
||||
|
|
||||
|
def __eq__(self, other): |
||||
|
if not isinstance(other, tzrange): |
||||
|
return False |
||||
|
return (self._std_abbr == other._std_abbr and |
||||
|
self._dst_abbr == other._dst_abbr and |
||||
|
self._std_offset == other._std_offset and |
||||
|
self._dst_offset == other._dst_offset and |
||||
|
self._start_delta == other._start_delta and |
||||
|
self._end_delta == other._end_delta) |
||||
|
|
||||
|
def __ne__(self, other): |
||||
|
return not self.__eq__(other) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s(...)" % self.__class__.__name__ |
||||
|
|
||||
|
__reduce__ = object.__reduce__ |
||||
|
|
||||
|
class tzstr(tzrange): |
||||
|
|
||||
|
def __init__(self, s): |
||||
|
global parser |
||||
|
if not parser: |
||||
|
from dateutil import parser |
||||
|
self._s = s |
||||
|
|
||||
|
res = parser._parsetz(s) |
||||
|
if res is None: |
||||
|
raise ValueError, "unknown string format" |
||||
|
|
||||
|
# Here we break the compatibility with the TZ variable handling. |
||||
|
# GMT-3 actually *means* the timezone -3. |
||||
|
if res.stdabbr in ("GMT", "UTC"): |
||||
|
res.stdoffset *= -1 |
||||
|
|
||||
|
# We must initialize it first, since _delta() needs |
||||
|
# _std_offset and _dst_offset set. Use False in start/end |
||||
|
# to avoid building it two times. |
||||
|
tzrange.__init__(self, res.stdabbr, res.stdoffset, |
||||
|
res.dstabbr, res.dstoffset, |
||||
|
start=False, end=False) |
||||
|
|
||||
|
if not res.dstabbr: |
||||
|
self._start_delta = None |
||||
|
self._end_delta = None |
||||
|
else: |
||||
|
self._start_delta = self._delta(res.start) |
||||
|
if self._start_delta: |
||||
|
self._end_delta = self._delta(res.end, isend=1) |
||||
|
|
||||
|
def _delta(self, x, isend=0): |
||||
|
kwargs = {} |
||||
|
if x.month is not None: |
||||
|
kwargs["month"] = x.month |
||||
|
if x.weekday is not None: |
||||
|
kwargs["weekday"] = relativedelta.weekday(x.weekday, x.week) |
||||
|
if x.week > 0: |
||||
|
kwargs["day"] = 1 |
||||
|
else: |
||||
|
kwargs["day"] = 31 |
||||
|
elif x.day: |
||||
|
kwargs["day"] = x.day |
||||
|
elif x.yday is not None: |
||||
|
kwargs["yearday"] = x.yday |
||||
|
elif x.jyday is not None: |
||||
|
kwargs["nlyearday"] = x.jyday |
||||
|
if not kwargs: |
||||
|
# Default is to start on first sunday of april, and end |
||||
|
# on last sunday of october. |
||||
|
if not isend: |
||||
|
kwargs["month"] = 4 |
||||
|
kwargs["day"] = 1 |
||||
|
kwargs["weekday"] = relativedelta.SU(+1) |
||||
|
else: |
||||
|
kwargs["month"] = 10 |
||||
|
kwargs["day"] = 31 |
||||
|
kwargs["weekday"] = relativedelta.SU(-1) |
||||
|
if x.time is not None: |
||||
|
kwargs["seconds"] = x.time |
||||
|
else: |
||||
|
# Default is 2AM. |
||||
|
kwargs["seconds"] = 7200 |
||||
|
if isend: |
||||
|
# Convert to standard time, to follow the documented way |
||||
|
# of working with the extra hour. See the documentation |
||||
|
# of the tzinfo class. |
||||
|
delta = self._dst_offset-self._std_offset |
||||
|
kwargs["seconds"] -= delta.seconds+delta.days*86400 |
||||
|
return relativedelta.relativedelta(**kwargs) |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s(%s)" % (self.__class__.__name__, `self._s`) |
||||
|
|
||||
|
class _tzicalvtzcomp: |
||||
|
def __init__(self, tzoffsetfrom, tzoffsetto, isdst, |
||||
|
tzname=None, rrule=None): |
||||
|
self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom) |
||||
|
self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto) |
||||
|
self.tzoffsetdiff = self.tzoffsetto-self.tzoffsetfrom |
||||
|
self.isdst = isdst |
||||
|
self.tzname = tzname |
||||
|
self.rrule = rrule |
||||
|
|
||||
|
class _tzicalvtz(datetime.tzinfo): |
||||
|
def __init__(self, tzid, comps=[]): |
||||
|
self._tzid = tzid |
||||
|
self._comps = comps |
||||
|
self._cachedate = [] |
||||
|
self._cachecomp = [] |
||||
|
|
||||
|
def _find_comp(self, dt): |
||||
|
if len(self._comps) == 1: |
||||
|
return self._comps[0] |
||||
|
dt = dt.replace(tzinfo=None) |
||||
|
try: |
||||
|
return self._cachecomp[self._cachedate.index(dt)] |
||||
|
except ValueError: |
||||
|
pass |
||||
|
lastcomp = None |
||||
|
lastcompdt = None |
||||
|
for comp in self._comps: |
||||
|
if not comp.isdst: |
||||
|
# Handle the extra hour in DST -> STD |
||||
|
compdt = comp.rrule.before(dt-comp.tzoffsetdiff, inc=True) |
||||
|
else: |
||||
|
compdt = comp.rrule.before(dt, inc=True) |
||||
|
if compdt and (not lastcompdt or lastcompdt < compdt): |
||||
|
lastcompdt = compdt |
||||
|
lastcomp = comp |
||||
|
if not lastcomp: |
||||
|
# RFC says nothing about what to do when a given |
||||
|
# time is before the first onset date. We'll look for the |
||||
|
# first standard component, or the first component, if |
||||
|
# none is found. |
||||
|
for comp in self._comps: |
||||
|
if not comp.isdst: |
||||
|
lastcomp = comp |
||||
|
break |
||||
|
else: |
||||
|
lastcomp = comp[0] |
||||
|
self._cachedate.insert(0, dt) |
||||
|
self._cachecomp.insert(0, lastcomp) |
||||
|
if len(self._cachedate) > 10: |
||||
|
self._cachedate.pop() |
||||
|
self._cachecomp.pop() |
||||
|
return lastcomp |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
return self._find_comp(dt).tzoffsetto |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
comp = self._find_comp(dt) |
||||
|
if comp.isdst: |
||||
|
return comp.tzoffsetdiff |
||||
|
else: |
||||
|
return ZERO |
||||
|
|
||||
|
def tzname(self, dt): |
||||
|
return self._find_comp(dt).tzname |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "<tzicalvtz %s>" % `self._tzid` |
||||
|
|
||||
|
__reduce__ = object.__reduce__ |
||||
|
|
||||
|
class tzical: |
||||
|
def __init__(self, fileobj): |
||||
|
global rrule |
||||
|
if not rrule: |
||||
|
from dateutil import rrule |
||||
|
|
||||
|
if isinstance(fileobj, basestring): |
||||
|
self._s = fileobj |
||||
|
fileobj = open(fileobj) |
||||
|
elif hasattr(fileobj, "name"): |
||||
|
self._s = fileobj.name |
||||
|
else: |
||||
|
self._s = `fileobj` |
||||
|
|
||||
|
self._vtz = {} |
||||
|
|
||||
|
self._parse_rfc(fileobj.read()) |
||||
|
|
||||
|
def keys(self): |
||||
|
return self._vtz.keys() |
||||
|
|
||||
|
def get(self, tzid=None): |
||||
|
if tzid is None: |
||||
|
keys = self._vtz.keys() |
||||
|
if len(keys) == 0: |
||||
|
raise ValueError, "no timezones defined" |
||||
|
elif len(keys) > 1: |
||||
|
raise ValueError, "more than one timezone available" |
||||
|
tzid = keys[0] |
||||
|
return self._vtz.get(tzid) |
||||
|
|
||||
|
def _parse_offset(self, s): |
||||
|
s = s.strip() |
||||
|
if not s: |
||||
|
raise ValueError, "empty offset" |
||||
|
if s[0] in ('+', '-'): |
||||
|
signal = (-1,+1)[s[0]=='+'] |
||||
|
s = s[1:] |
||||
|
else: |
||||
|
signal = +1 |
||||
|
if len(s) == 4: |
||||
|
return (int(s[:2])*3600+int(s[2:])*60)*signal |
||||
|
elif len(s) == 6: |
||||
|
return (int(s[:2])*3600+int(s[2:4])*60+int(s[4:]))*signal |
||||
|
else: |
||||
|
raise ValueError, "invalid offset: "+s |
||||
|
|
||||
|
def _parse_rfc(self, s): |
||||
|
lines = s.splitlines() |
||||
|
if not lines: |
||||
|
raise ValueError, "empty string" |
||||
|
|
||||
|
# Unfold |
||||
|
i = 0 |
||||
|
while i < len(lines): |
||||
|
line = lines[i].rstrip() |
||||
|
if not line: |
||||
|
del lines[i] |
||||
|
elif i > 0 and line[0] == " ": |
||||
|
lines[i-1] += line[1:] |
||||
|
del lines[i] |
||||
|
else: |
||||
|
i += 1 |
||||
|
|
||||
|
tzid = None |
||||
|
comps = [] |
||||
|
invtz = False |
||||
|
comptype = None |
||||
|
for line in lines: |
||||
|
if not line: |
||||
|
continue |
||||
|
name, value = line.split(':', 1) |
||||
|
parms = name.split(';') |
||||
|
if not parms: |
||||
|
raise ValueError, "empty property name" |
||||
|
name = parms[0].upper() |
||||
|
parms = parms[1:] |
||||
|
if invtz: |
||||
|
if name == "BEGIN": |
||||
|
if value in ("STANDARD", "DAYLIGHT"): |
||||
|
# Process component |
||||
|
pass |
||||
|
else: |
||||
|
raise ValueError, "unknown component: "+value |
||||
|
comptype = value |
||||
|
founddtstart = False |
||||
|
tzoffsetfrom = None |
||||
|
tzoffsetto = None |
||||
|
rrulelines = [] |
||||
|
tzname = None |
||||
|
elif name == "END": |
||||
|
if value == "VTIMEZONE": |
||||
|
if comptype: |
||||
|
raise ValueError, \ |
||||
|
"component not closed: "+comptype |
||||
|
if not tzid: |
||||
|
raise ValueError, \ |
||||
|
"mandatory TZID not found" |
||||
|
if not comps: |
||||
|
raise ValueError, \ |
||||
|
"at least one component is needed" |
||||
|
# Process vtimezone |
||||
|
self._vtz[tzid] = _tzicalvtz(tzid, comps) |
||||
|
invtz = False |
||||
|
elif value == comptype: |
||||
|
if not founddtstart: |
||||
|
raise ValueError, \ |
||||
|
"mandatory DTSTART not found" |
||||
|
if tzoffsetfrom is None: |
||||
|
raise ValueError, \ |
||||
|
"mandatory TZOFFSETFROM not found" |
||||
|
if tzoffsetto is None: |
||||
|
raise ValueError, \ |
||||
|
"mandatory TZOFFSETFROM not found" |
||||
|
# Process component |
||||
|
rr = None |
||||
|
if rrulelines: |
||||
|
rr = rrule.rrulestr("\n".join(rrulelines), |
||||
|
compatible=True, |
||||
|
ignoretz=True, |
||||
|
cache=True) |
||||
|
comp = _tzicalvtzcomp(tzoffsetfrom, tzoffsetto, |
||||
|
(comptype == "DAYLIGHT"), |
||||
|
tzname, rr) |
||||
|
comps.append(comp) |
||||
|
comptype = None |
||||
|
else: |
||||
|
raise ValueError, \ |
||||
|
"invalid component end: "+value |
||||
|
elif comptype: |
||||
|
if name == "DTSTART": |
||||
|
rrulelines.append(line) |
||||
|
founddtstart = True |
||||
|
elif name in ("RRULE", "RDATE", "EXRULE", "EXDATE"): |
||||
|
rrulelines.append(line) |
||||
|
elif name == "TZOFFSETFROM": |
||||
|
if parms: |
||||
|
raise ValueError, \ |
||||
|
"unsupported %s parm: %s "%(name, parms[0]) |
||||
|
tzoffsetfrom = self._parse_offset(value) |
||||
|
elif name == "TZOFFSETTO": |
||||
|
if parms: |
||||
|
raise ValueError, \ |
||||
|
"unsupported TZOFFSETTO parm: "+parms[0] |
||||
|
tzoffsetto = self._parse_offset(value) |
||||
|
elif name == "TZNAME": |
||||
|
if parms: |
||||
|
raise ValueError, \ |
||||
|
"unsupported TZNAME parm: "+parms[0] |
||||
|
tzname = value |
||||
|
elif name == "COMMENT": |
||||
|
pass |
||||
|
else: |
||||
|
raise ValueError, "unsupported property: "+name |
||||
|
else: |
||||
|
if name == "TZID": |
||||
|
if parms: |
||||
|
raise ValueError, \ |
||||
|
"unsupported TZID parm: "+parms[0] |
||||
|
tzid = value |
||||
|
elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"): |
||||
|
pass |
||||
|
else: |
||||
|
raise ValueError, "unsupported property: "+name |
||||
|
elif name == "BEGIN" and value == "VTIMEZONE": |
||||
|
tzid = None |
||||
|
comps = [] |
||||
|
invtz = True |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "%s(%s)" % (self.__class__.__name__, `self._s`) |
||||
|
|
||||
|
if sys.platform != "win32": |
||||
|
TZFILES = ["/etc/localtime", "localtime"] |
||||
|
TZPATHS = ["/usr/share/zoneinfo", "/usr/lib/zoneinfo", "/etc/zoneinfo"] |
||||
|
else: |
||||
|
TZFILES = [] |
||||
|
TZPATHS = [] |
||||
|
|
||||
|
def gettz(name=None): |
||||
|
tz = None |
||||
|
if not name: |
||||
|
try: |
||||
|
name = os.environ["TZ"] |
||||
|
except KeyError: |
||||
|
pass |
||||
|
if name is None or name == ":": |
||||
|
for filepath in TZFILES: |
||||
|
if not os.path.isabs(filepath): |
||||
|
filename = filepath |
||||
|
for path in TZPATHS: |
||||
|
filepath = os.path.join(path, filename) |
||||
|
if os.path.isfile(filepath): |
||||
|
break |
||||
|
else: |
||||
|
continue |
||||
|
if os.path.isfile(filepath): |
||||
|
try: |
||||
|
tz = tzfile(filepath) |
||||
|
break |
||||
|
except (IOError, OSError, ValueError): |
||||
|
pass |
||||
|
else: |
||||
|
tz = tzlocal() |
||||
|
else: |
||||
|
if name.startswith(":"): |
||||
|
name = name[:-1] |
||||
|
if os.path.isabs(name): |
||||
|
if os.path.isfile(name): |
||||
|
tz = tzfile(name) |
||||
|
else: |
||||
|
tz = None |
||||
|
else: |
||||
|
for path in TZPATHS: |
||||
|
filepath = os.path.join(path, name) |
||||
|
if not os.path.isfile(filepath): |
||||
|
filepath = filepath.replace(' ','_') |
||||
|
if not os.path.isfile(filepath): |
||||
|
continue |
||||
|
try: |
||||
|
tz = tzfile(filepath) |
||||
|
break |
||||
|
except (IOError, OSError, ValueError): |
||||
|
pass |
||||
|
else: |
||||
|
tz = None |
||||
|
if tzwin: |
||||
|
try: |
||||
|
tz = tzwin(name) |
||||
|
except OSError: |
||||
|
pass |
||||
|
if not tz: |
||||
|
from dateutil.zoneinfo import gettz |
||||
|
tz = gettz(name) |
||||
|
if not tz: |
||||
|
for c in name: |
||||
|
# name must have at least one offset to be a tzstr |
||||
|
if c in "0123456789": |
||||
|
try: |
||||
|
tz = tzstr(name) |
||||
|
except ValueError: |
||||
|
pass |
||||
|
break |
||||
|
else: |
||||
|
if name in ("GMT", "UTC"): |
||||
|
tz = tzutc() |
||||
|
elif name in time.tzname: |
||||
|
tz = tzlocal() |
||||
|
return tz |
||||
|
|
||||
|
# vim:ts=4:sw=4:et |
@ -0,0 +1,180 @@ |
|||||
|
# This code was originally contributed by Jeffrey Harris. |
||||
|
import datetime |
||||
|
import struct |
||||
|
import _winreg |
||||
|
|
||||
|
__author__ = "Jeffrey Harris & Gustavo Niemeyer <gustavo@niemeyer.net>" |
||||
|
|
||||
|
__all__ = ["tzwin", "tzwinlocal"] |
||||
|
|
||||
|
ONEWEEK = datetime.timedelta(7) |
||||
|
|
||||
|
TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones" |
||||
|
TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones" |
||||
|
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation" |
||||
|
|
||||
|
def _settzkeyname(): |
||||
|
global TZKEYNAME |
||||
|
handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) |
||||
|
try: |
||||
|
_winreg.OpenKey(handle, TZKEYNAMENT).Close() |
||||
|
TZKEYNAME = TZKEYNAMENT |
||||
|
except WindowsError: |
||||
|
TZKEYNAME = TZKEYNAME9X |
||||
|
handle.Close() |
||||
|
|
||||
|
_settzkeyname() |
||||
|
|
||||
|
class tzwinbase(datetime.tzinfo): |
||||
|
"""tzinfo class based on win32's timezones available in the registry.""" |
||||
|
|
||||
|
def utcoffset(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return datetime.timedelta(minutes=self._dstoffset) |
||||
|
else: |
||||
|
return datetime.timedelta(minutes=self._stdoffset) |
||||
|
|
||||
|
def dst(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
minutes = self._dstoffset - self._stdoffset |
||||
|
return datetime.timedelta(minutes=minutes) |
||||
|
else: |
||||
|
return datetime.timedelta(0) |
||||
|
|
||||
|
def tzname(self, dt): |
||||
|
if self._isdst(dt): |
||||
|
return self._dstname |
||||
|
else: |
||||
|
return self._stdname |
||||
|
|
||||
|
def list(): |
||||
|
"""Return a list of all time zones known to the system.""" |
||||
|
handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) |
||||
|
tzkey = _winreg.OpenKey(handle, TZKEYNAME) |
||||
|
result = [_winreg.EnumKey(tzkey, i) |
||||
|
for i in range(_winreg.QueryInfoKey(tzkey)[0])] |
||||
|
tzkey.Close() |
||||
|
handle.Close() |
||||
|
return result |
||||
|
list = staticmethod(list) |
||||
|
|
||||
|
def display(self): |
||||
|
return self._display |
||||
|
|
||||
|
def _isdst(self, dt): |
||||
|
dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek, |
||||
|
self._dsthour, self._dstminute, |
||||
|
self._dstweeknumber) |
||||
|
dstoff = picknthweekday(dt.year, self._stdmonth, self._stddayofweek, |
||||
|
self._stdhour, self._stdminute, |
||||
|
self._stdweeknumber) |
||||
|
if dston < dstoff: |
||||
|
return dston <= dt.replace(tzinfo=None) < dstoff |
||||
|
else: |
||||
|
return not dstoff <= dt.replace(tzinfo=None) < dston |
||||
|
|
||||
|
|
||||
|
class tzwin(tzwinbase): |
||||
|
|
||||
|
def __init__(self, name): |
||||
|
self._name = name |
||||
|
|
||||
|
handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) |
||||
|
tzkey = _winreg.OpenKey(handle, "%s\%s" % (TZKEYNAME, name)) |
||||
|
keydict = valuestodict(tzkey) |
||||
|
tzkey.Close() |
||||
|
handle.Close() |
||||
|
|
||||
|
self._stdname = keydict["Std"].encode("iso-8859-1") |
||||
|
self._dstname = keydict["Dlt"].encode("iso-8859-1") |
||||
|
|
||||
|
self._display = keydict["Display"] |
||||
|
|
||||
|
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm |
||||
|
tup = struct.unpack("=3l16h", keydict["TZI"]) |
||||
|
self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1 |
||||
|
self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1 |
||||
|
|
||||
|
(self._stdmonth, |
||||
|
self._stddayofweek, # Sunday = 0 |
||||
|
self._stdweeknumber, # Last = 5 |
||||
|
self._stdhour, |
||||
|
self._stdminute) = tup[4:9] |
||||
|
|
||||
|
(self._dstmonth, |
||||
|
self._dstdayofweek, # Sunday = 0 |
||||
|
self._dstweeknumber, # Last = 5 |
||||
|
self._dsthour, |
||||
|
self._dstminute) = tup[12:17] |
||||
|
|
||||
|
def __repr__(self): |
||||
|
return "tzwin(%s)" % repr(self._name) |
||||
|
|
||||
|
def __reduce__(self): |
||||
|
return (self.__class__, (self._name,)) |
||||
|
|
||||
|
|
||||
|
class tzwinlocal(tzwinbase): |
||||
|
|
||||
|
def __init__(self): |
||||
|
|
||||
|
handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE) |
||||
|
|
||||
|
tzlocalkey = _winreg.OpenKey(handle, TZLOCALKEYNAME) |
||||
|
keydict = valuestodict(tzlocalkey) |
||||
|
tzlocalkey.Close() |
||||
|
|
||||
|
self._stdname = keydict["StandardName"].encode("iso-8859-1") |
||||
|
self._dstname = keydict["DaylightName"].encode("iso-8859-1") |
||||
|
|
||||
|
try: |
||||
|
tzkey = _winreg.OpenKey(handle, "%s\%s"%(TZKEYNAME, self._stdname)) |
||||
|
_keydict = valuestodict(tzkey) |
||||
|
self._display = _keydict["Display"] |
||||
|
tzkey.Close() |
||||
|
except OSError: |
||||
|
self._display = None |
||||
|
|
||||
|
handle.Close() |
||||
|
|
||||
|
self._stdoffset = -keydict["Bias"]-keydict["StandardBias"] |
||||
|
self._dstoffset = self._stdoffset-keydict["DaylightBias"] |
||||
|
|
||||
|
|
||||
|
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm |
||||
|
tup = struct.unpack("=8h", keydict["StandardStart"]) |
||||
|
|
||||
|
(self._stdmonth, |
||||
|
self._stddayofweek, # Sunday = 0 |
||||
|
self._stdweeknumber, # Last = 5 |
||||
|
self._stdhour, |
||||
|
self._stdminute) = tup[1:6] |
||||
|
|
||||
|
tup = struct.unpack("=8h", keydict["DaylightStart"]) |
||||
|
|
||||
|
(self._dstmonth, |
||||
|
self._dstdayofweek, # Sunday = 0 |
||||
|
self._dstweeknumber, # Last = 5 |
||||
|
self._dsthour, |
||||
|
self._dstminute) = tup[1:6] |
||||
|
|
||||
|
def __reduce__(self): |
||||
|
return (self.__class__, ()) |
||||
|
|
||||
|
def picknthweekday(year, month, dayofweek, hour, minute, whichweek): |
||||
|
"""dayofweek == 0 means Sunday, whichweek 5 means last instance""" |
||||
|
first = datetime.datetime(year, month, 1, hour, minute) |
||||
|
weekdayone = first.replace(day=((dayofweek-first.isoweekday())%7+1)) |
||||
|
for n in xrange(whichweek): |
||||
|
dt = weekdayone+(whichweek-n)*ONEWEEK |
||||
|
if dt.month == month: |
||||
|
return dt |
||||
|
|
||||
|
def valuestodict(key): |
||||
|
"""Convert a registry key's values to a dictionary.""" |
||||
|
dict = {} |
||||
|
size = _winreg.QueryInfoKey(key)[1] |
||||
|
for i in range(size): |
||||
|
data = _winreg.EnumValue(key, i) |
||||
|
dict[data[0]] = data[1] |
||||
|
return dict |
@ -0,0 +1,87 @@ |
|||||
|
""" |
||||
|
Copyright (c) 2003-2005 Gustavo Niemeyer <gustavo@niemeyer.net> |
||||
|
|
||||
|
This module offers extensions to the standard python 2.3+ |
||||
|
datetime module. |
||||
|
""" |
||||
|
from dateutil.tz import tzfile |
||||
|
from tarfile import TarFile |
||||
|
import os |
||||
|
|
||||
|
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>" |
||||
|
__license__ = "PSF License" |
||||
|
|
||||
|
__all__ = ["setcachesize", "gettz", "rebuild"] |
||||
|
|
||||
|
CACHE = [] |
||||
|
CACHESIZE = 10 |
||||
|
|
||||
|
class tzfile(tzfile): |
||||
|
def __reduce__(self): |
||||
|
return (gettz, (self._filename,)) |
||||
|
|
||||
|
def getzoneinfofile(): |
||||
|
filenames = os.listdir(os.path.join(os.path.dirname(__file__))) |
||||
|
filenames.sort() |
||||
|
filenames.reverse() |
||||
|
for entry in filenames: |
||||
|
if entry.startswith("zoneinfo") and ".tar." in entry: |
||||
|
return os.path.join(os.path.dirname(__file__), entry) |
||||
|
return None |
||||
|
|
||||
|
ZONEINFOFILE = getzoneinfofile() |
||||
|
|
||||
|
del getzoneinfofile |
||||
|
|
||||
|
def setcachesize(size): |
||||
|
global CACHESIZE, CACHE |
||||
|
CACHESIZE = size |
||||
|
del CACHE[size:] |
||||
|
|
||||
|
def gettz(name): |
||||
|
tzinfo = None |
||||
|
if ZONEINFOFILE: |
||||
|
for cachedname, tzinfo in CACHE: |
||||
|
if cachedname == name: |
||||
|
break |
||||
|
else: |
||||
|
tf = TarFile.open(ZONEINFOFILE) |
||||
|
try: |
||||
|
zonefile = tf.extractfile(name) |
||||
|
except KeyError: |
||||
|
tzinfo = None |
||||
|
else: |
||||
|
tzinfo = tzfile(zonefile) |
||||
|
tf.close() |
||||
|
CACHE.insert(0, (name, tzinfo)) |
||||
|
del CACHE[CACHESIZE:] |
||||
|
return tzinfo |
||||
|
|
||||
|
def rebuild(filename, tag=None, format="gz"): |
||||
|
import tempfile, shutil |
||||
|
tmpdir = tempfile.mkdtemp() |
||||
|
zonedir = os.path.join(tmpdir, "zoneinfo") |
||||
|
moduledir = os.path.dirname(__file__) |
||||
|
if tag: tag = "-"+tag |
||||
|
targetname = "zoneinfo%s.tar.%s" % (tag, format) |
||||
|
try: |
||||
|
tf = TarFile.open(filename) |
||||
|
for name in tf.getnames(): |
||||
|
if not (name.endswith(".sh") or |
||||
|
name.endswith(".tab") or |
||||
|
name == "leapseconds"): |
||||
|
tf.extract(name, tmpdir) |
||||
|
filepath = os.path.join(tmpdir, name) |
||||
|
os.system("zic -d %s %s" % (zonedir, filepath)) |
||||
|
tf.close() |
||||
|
target = os.path.join(moduledir, targetname) |
||||
|
for entry in os.listdir(moduledir): |
||||
|
if entry.startswith("zoneinfo") and ".tar." in entry: |
||||
|
os.unlink(os.path.join(moduledir, entry)) |
||||
|
tf = TarFile.open(target, "w:%s" % format) |
||||
|
for entry in os.listdir(zonedir): |
||||
|
entrypath = os.path.join(zonedir, entry) |
||||
|
tf.add(entrypath, entry) |
||||
|
tf.close() |
||||
|
finally: |
||||
|
shutil.rmtree(tmpdir) |
Binary file not shown.
Loading…
Reference in new issue