Update dateutil to latest version and include all files

Signed-off-by: James Vega <jamessan@users.sourceforge.net>
This commit is contained in:
James Vega 2009-05-11 12:16:28 -04:00
parent fea4cc2962
commit 5fda0df4a9
9 changed files with 1653 additions and 106 deletions

View File

@ -1,8 +1,9 @@
""" """
Copyright (c) 2003 Gustavo Niemeyer <niemeyer@conectiva.com> Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard python 2.3+
datetime module. datetime module.
""" """
__author__ = "Gustavo Niemeyer <niemeyer@conectiva.com>" __author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
__license__ = "PSF License" __license__ = "PSF License"
__version__ = "1.4.1"

View File

@ -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))

View File

@ -1,26 +1,31 @@
# -*- coding:iso-8859-1 -*- # -*- coding:iso-8859-1 -*-
# Gotten from: http://moin.conectiva.com.br/DateUtil
""" """
Copyright (c) 2003 Gustavo Niemeyer <niemeyer@conectiva.com> Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard python 2.3+
datetime module. datetime module.
""" """
__author__ = "Gustavo Niemeyer <niemeyer@conectiva.com>" __author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
__license__ = "PSF License" __license__ = "PSF License"
import os.path
import string
import sys
import time
import datetime import datetime
import string
import time
import sys
import os
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
import relativedelta import relativedelta
import tz import tz
__all__ = ["parse", "parserinfo"] __all__ = ["parse", "parserinfo"]
# Some pointers: # Some pointers:
# #
# http://www.cl.cam.ac.uk/~mgk25/iso-time.html # http://www.cl.cam.ac.uk/~mgk25/iso-time.html
@ -30,12 +35,9 @@ __all__ = ["parse", "parserinfo"]
# http://search.cpan.org/author/MUIR/Time-modules-2003.0211/lib/Time/ParseDate.pm # 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 # http://stein.cshl.org/jade/distrib/docs/java.text.SimpleDateFormat.html
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
class _timelex: class _timelex(object):
def __init__(self, instream): def __init__(self, instream):
if isinstance(instream, basestring): if isinstance(instream, basestring):
instream = StringIO(instream) instream = StringIO(instream)
@ -64,6 +66,8 @@ class _timelex:
nextchar = self.charstack.pop(0) nextchar = self.charstack.pop(0)
else: else:
nextchar = self.instream.read(1) nextchar = self.instream.read(1)
while nextchar == '\x00':
nextchar = self.instream.read(1)
if not nextchar: if not nextchar:
self.eof = True self.eof = True
break break
@ -139,6 +143,7 @@ class _timelex:
return list(cls(s)) return list(cls(s))
split = classmethod(split) split = classmethod(split)
class _resultbase(object): class _resultbase(object):
def __init__(self): def __init__(self):
@ -156,7 +161,8 @@ class _resultbase(object):
def __repr__(self): def __repr__(self):
return self._repr(self.__class__.__name__) return self._repr(self.__class__.__name__)
class parserinfo:
class parserinfo(object):
# m from a.m/p.m, t from ISO T separator # m from a.m/p.m, t from ISO T separator
JUMP = [" ", ".", ",", ";", "-", "/", "'", JUMP = [" ", ".", ",", ";", "-", "/", "'",
@ -204,7 +210,7 @@ class parserinfo:
self.yearfirst = yearfirst self.yearfirst = yearfirst
self._year = time.localtime().tm_year self._year = time.localtime().tm_year
self._century = self._year/100*100 self._century = self._year//100*100
def _convert(self, lst): def _convert(self, lst):
dct = {} dct = {}
@ -281,10 +287,10 @@ class parserinfo:
return True return True
class parser: class parser(object):
def __init__(self, parserinfo=parserinfo): def __init__(self, info=None):
self.info = parserinfo() self.info = info or parserinfo()
def parse(self, timestr, default=None, def parse(self, timestr, default=None,
ignoretz=False, tzinfos=None, ignoretz=False, tzinfos=None,
@ -355,15 +361,18 @@ class parser:
# Check if it's a number # Check if it's a number
try: try:
value = float(l[i]) value_repr = l[i]
value = float(value_repr)
except ValueError: except ValueError:
value = None value = None
if value is not None: if value is not None:
# Token is a number # Token is a number
len_li = len(l[i]) len_li = len(l[i])
i += 1 i += 1
if (len(ymd) == 3 and len_li in (2, 4) if (len(ymd) == 3 and len_li in (2, 4)
and (i >= len_l or l[i] != ':')): and (i >= len_l or (l[i] != ':' and
info.hms(l[i]) is None))):
# 19990101T23[59] # 19990101T23[59]
s = l[i-1] s = l[i-1]
res.hour = int(s[:2]) res.hour = int(s[:2])
@ -380,10 +389,7 @@ class parser:
# 19990101T235959[.59] # 19990101T235959[.59]
res.hour = int(s[:2]) res.hour = int(s[:2])
res.minute = int(s[2:4]) res.minute = int(s[2:4])
value = float(s[4:]) res.second, res.microsecond = _parsems(s[4:])
res.second = int(value)
if value%1:
res.microsecond = int(1000000*(value%1))
elif len_li == 8: elif len_li == 8:
# YYYYMMDD # YYYYMMDD
s = l[i-1] s = l[i-1]
@ -417,15 +423,15 @@ class parser:
if value%1: if value%1:
res.second = int(60*(value%1)) res.second = int(60*(value%1))
elif idx == 2: elif idx == 2:
res.second = int(value) res.second, res.microsecond = \
if value%1: _parsems(value_repr)
res.microsecond = int(1000000*(value%1))
i += 1 i += 1
if i >= len_l or idx == 2: if i >= len_l or idx == 2:
break break
# 12h00 # 12h00
try: try:
value = float(l[i]) value_repr = l[i]
value = float(value_repr)
except ValueError: except ValueError:
break break
else: else:
@ -445,10 +451,7 @@ class parser:
res.second = int(60*(value%1)) res.second = int(60*(value%1))
i += 1 i += 1
if i < len_l and l[i] == ':': if i < len_l and l[i] == ':':
value = float(l[i+1]) res.second, res.microsecond = _parsems(l[i+1])
res.second = int(value)
if value%1:
res.microsecond = int(1000000*(value%1))
i += 2 i += 2
elif i < len_l and l[i] in ('-', '/', '.'): elif i < len_l and l[i] in ('-', '/', '.'):
sep = l[i] sep = l[i]
@ -693,7 +696,8 @@ def parse(timestr, parserinfo=None, **kwargs):
else: else:
return DEFAULTPARSER.parse(timestr, **kwargs) return DEFAULTPARSER.parse(timestr, **kwargs)
class _tzparser:
class _tzparser(object):
class _result(_resultbase): class _result(_resultbase):
@ -737,6 +741,8 @@ class _tzparser:
if (i < len_l and if (i < len_l and
(l[i] in ('+', '-') or l[i][0] in "0123456789")): (l[i] in ('+', '-') or l[i][0] in "0123456789")):
if l[i] in ('+', '-'): if l[i] in ('+', '-'):
# Yes, that's right. See the TZ variable
# documentation.
signal = (1,-1)[l[i] == '+'] signal = (1,-1)[l[i] == '+']
i += 1 i += 1
else: else:
@ -862,8 +868,19 @@ class _tzparser:
return res return res
DEFAULTTZPARSER = _tzparser() DEFAULTTZPARSER = _tzparser()
def _parsetz(tzstr): def _parsetz(tzstr):
return DEFAULTTZPARSER.parse(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 # vim:ts=4:sw=4:et

View File

@ -1,10 +1,10 @@
""" """
Copyright (c) 2003 Gustavo Niemeyer <niemeyer@conectiva.com> Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard python 2.3+
datetime module. datetime module.
""" """
__author__ = "Gustavo Niemeyer <niemeyer@conectiva.com>" __author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
__license__ = "PSF License" __license__ = "PSF License"
import datetime import datetime
@ -15,7 +15,7 @@ __all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
class weekday(object): class weekday(object):
__slots__ = ["weekday", "n"] __slots__ = ["weekday", "n"]
def __init__(self, weekday, n=0): def __init__(self, weekday, n=None):
self.weekday = weekday self.weekday = weekday
self.n = n self.n = n
@ -201,27 +201,27 @@ Here is the behavior of operations with relativedelta:
def _fix(self): def _fix(self):
if abs(self.microseconds) > 999999: if abs(self.microseconds) > 999999:
s = self.microseconds/abs(self.microseconds) s = self.microseconds//abs(self.microseconds)
div, mod = divmod(self.microseconds*s, 1000000) div, mod = divmod(self.microseconds*s, 1000000)
self.microseconds = mod*s self.microseconds = mod*s
self.seconds += div*s self.seconds += div*s
if abs(self.seconds) > 59: if abs(self.seconds) > 59:
s = self.seconds/abs(self.seconds) s = self.seconds//abs(self.seconds)
div, mod = divmod(self.seconds*s, 60) div, mod = divmod(self.seconds*s, 60)
self.seconds = mod*s self.seconds = mod*s
self.minutes += div*s self.minutes += div*s
if abs(self.minutes) > 59: if abs(self.minutes) > 59:
s = self.minutes/abs(self.minutes) s = self.minutes//abs(self.minutes)
div, mod = divmod(self.minutes*s, 60) div, mod = divmod(self.minutes*s, 60)
self.minutes = mod*s self.minutes = mod*s
self.hours += div*s self.hours += div*s
if abs(self.hours) > 23: if abs(self.hours) > 23:
s = self.hours/abs(self.hours) s = self.hours//abs(self.hours)
div, mod = divmod(self.hours*s, 24) div, mod = divmod(self.hours*s, 24)
self.hours = mod*s self.hours = mod*s
self.days += div*s self.days += div*s
if abs(self.months) > 11: if abs(self.months) > 11:
s = self.months/abs(self.months) s = self.months//abs(self.months)
div, mod = divmod(self.months*s, 12) div, mod = divmod(self.months*s, 12)
self.months = mod*s self.months = mod*s
self.years += div*s self.years += div*s
@ -235,7 +235,7 @@ Here is the behavior of operations with relativedelta:
def _set_months(self, months): def _set_months(self, months):
self.months = months self.months = months
if abs(self.months) > 11: if abs(self.months) > 11:
s = self.months/abs(self.months) s = self.months//abs(self.months)
div, mod = divmod(self.months*s, 12) div, mod = divmod(self.months*s, 12)
self.months = mod*s self.months = mod*s
self.years = div*s self.years = div*s
@ -392,7 +392,7 @@ Here is the behavior of operations with relativedelta:
if self.weekday.weekday != other.weekday.weekday: if self.weekday.weekday != other.weekday.weekday:
return False return False
n1, n2 = self.weekday.n, other.weekday.n n1, n2 = self.weekday.n, other.weekday.n
if n1 != n2 and not (n1 in (0, 1) and n2 in (0, 1)): if n1 != n2 and not ((not n1 or n1 == 1) and (not n2 or n2 == 1)):
return False return False
return (self.years == other.years and return (self.years == other.years and
self.months == other.months and self.months == other.months and

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,29 @@
""" """
Copyright (c) 2003 Gustavo Niemeyer <niemeyer@conectiva.com> Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
This module offers extensions to the standard python 2.3+ This module offers extensions to the standard python 2.3+
datetime module. datetime module.
""" """
__author__ = "Gustavo Niemeyer <niemeyer@conectiva.com>" __author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
__license__ = "PSF License" __license__ = "PSF License"
import datetime import datetime
import struct import struct
import time import time
import sys
import os
import relativedelta relativedelta = None
import parser parser = None
rrule = None # XXX Where does this come from? Why isn't it here? rrule = None
__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", __all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
"tzrange", "tzstr", "tzical", "gettz"] "tzstr", "tzical", "tzwin", "tzwinlocal", "gettz"]
try:
from dateutil.tzwin import tzwin, tzwinlocal
except (ImportError, OSError):
tzwin, tzwinlocal = None, None
ZERO = datetime.timedelta(0) ZERO = datetime.timedelta(0)
EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal() EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal()
@ -42,6 +49,8 @@ class tzutc(datetime.tzinfo):
def __repr__(self): def __repr__(self):
return "%s()" % self.__class__.__name__ return "%s()" % self.__class__.__name__
__reduce__ = object.__reduce__
class tzoffset(datetime.tzinfo): class tzoffset(datetime.tzinfo):
def __init__(self, name, offset): def __init__(self, name, offset):
@ -69,6 +78,8 @@ class tzoffset(datetime.tzinfo):
`self._name`, `self._name`,
self._offset.days*86400+self._offset.seconds) self._offset.days*86400+self._offset.seconds)
__reduce__ = object.__reduce__
class tzlocal(datetime.tzinfo): class tzlocal(datetime.tzinfo):
_std_offset = datetime.timedelta(seconds=-time.timezone) _std_offset = datetime.timedelta(seconds=-time.timezone)
@ -136,6 +147,8 @@ class tzlocal(datetime.tzinfo):
def __repr__(self): def __repr__(self):
return "%s()" % self.__class__.__name__ return "%s()" % self.__class__.__name__
__reduce__ = object.__reduce__
class _ttinfo(object): class _ttinfo(object):
__slots__ = ["offset", "delta", "isdst", "abbr", "isstd", "isgmt"] __slots__ = ["offset", "delta", "isdst", "abbr", "isstd", "isgmt"]
@ -164,6 +177,17 @@ class _ttinfo(object):
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(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): class tzfile(datetime.tzinfo):
# http://www.twinsun.com/tz/tz-link.htm # http://www.twinsun.com/tz/tz-link.htm
@ -171,12 +195,12 @@ class tzfile(datetime.tzinfo):
def __init__(self, fileobj): def __init__(self, fileobj):
if isinstance(fileobj, basestring): if isinstance(fileobj, basestring):
self._s = fileobj self._filename = fileobj
fileobj = open(fileobj) fileobj = open(fileobj)
elif hasattr(fileobj, "name"): elif hasattr(fileobj, "name"):
self._s = fileobj.name self._filename = fileobj.name
else: else:
self._s = `fileobj` self._filename = `fileobj`
# From tzfile(5): # From tzfile(5):
# #
@ -273,7 +297,7 @@ class tzfile(datetime.tzinfo):
# Not used, for now # Not used, for now
if leapcnt: if leapcnt:
leap = struct.unpack(">%dl" % leapcnt*2, leap = struct.unpack(">%dl" % (leapcnt*2),
fileobj.read(leapcnt*8)) fileobj.read(leapcnt*8))
# Then there are tzh_ttisstdcnt standard/wall # Then there are tzh_ttisstdcnt standard/wall
@ -305,11 +329,16 @@ class tzfile(datetime.tzinfo):
# Build ttinfo list # Build ttinfo list
self._ttinfo_list = [] self._ttinfo_list = []
for i in range(typecnt): 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 = _ttinfo()
tti.offset = ttinfo[i][0] tti.offset = gmtoff
tti.delta = datetime.timedelta(seconds=ttinfo[i][0]) tti.delta = datetime.timedelta(seconds=gmtoff)
tti.isdst = ttinfo[i][1] tti.isdst = isdst
tti.abbr = abbr[ttinfo[i][2]:abbr.find('\x00', ttinfo[i][2])] tti.abbr = abbr[abbrind:abbr.find('\x00', abbrind)]
tti.isstd = (ttisstdcnt > i and isstd[i] != 0) tti.isstd = (ttisstdcnt > i and isstd[i] != 0)
tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0) tti.isgmt = (ttisgmtcnt > i and isgmt[i] != 0)
self._ttinfo_list.append(tti) self._ttinfo_list.append(tti)
@ -409,7 +438,7 @@ class tzfile(datetime.tzinfo):
# The documentation says that utcoffset()-dst() must # The documentation says that utcoffset()-dst() must
# be constant for every dt. # be constant for every dt.
return self._find_ttinfo(dt, laststd=1).delta-tti.delta return tti.delta-self._find_ttinfo(dt, laststd=1).delta
# An alternative for that would be: # An alternative for that would be:
# #
@ -436,13 +465,21 @@ class tzfile(datetime.tzinfo):
def __repr__(self): def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, `self._s`) 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): class tzrange(datetime.tzinfo):
def __init__(self, stdabbr, stdoffset=None, def __init__(self, stdabbr, stdoffset=None,
dstabbr=None, dstoffset=None, dstabbr=None, dstoffset=None,
start=None, end=None): start=None, end=None):
global relativedelta
if not relativedelta:
from dateutil import relativedelta
self._std_abbr = stdabbr self._std_abbr = stdabbr
self._dst_abbr = dstabbr self._dst_abbr = dstabbr
if stdoffset is not None: if stdoffset is not None:
@ -455,12 +492,12 @@ class tzrange(datetime.tzinfo):
self._dst_offset = self._std_offset+datetime.timedelta(hours=+1) self._dst_offset = self._std_offset+datetime.timedelta(hours=+1)
else: else:
self._dst_offset = ZERO self._dst_offset = ZERO
if start is None: if dstabbr and start is None:
self._start_delta = relativedelta.relativedelta( self._start_delta = relativedelta.relativedelta(
hours=+2, month=4, day=1, weekday=relativedelta.SU(+1)) hours=+2, month=4, day=1, weekday=relativedelta.SU(+1))
else: else:
self._start_delta = start self._start_delta = start
if end is None: if dstabbr and end is None:
self._end_delta = relativedelta.relativedelta( self._end_delta = relativedelta.relativedelta(
hours=+1, month=10, day=31, weekday=relativedelta.SU(-1)) hours=+1, month=10, day=31, weekday=relativedelta.SU(-1))
else: else:
@ -487,7 +524,7 @@ class tzrange(datetime.tzinfo):
def _isdst(self, dt): def _isdst(self, dt):
if not self._start_delta: if not self._start_delta:
return False return False
year = datetime.date(dt.year,1,1) year = datetime.datetime(dt.year,1,1)
start = year+self._start_delta start = year+self._start_delta
end = year+self._end_delta end = year+self._end_delta
dt = dt.replace(tzinfo=None) dt = dt.replace(tzinfo=None)
@ -512,16 +549,25 @@ class tzrange(datetime.tzinfo):
def __repr__(self): def __repr__(self):
return "%s(...)" % self.__class__.__name__ return "%s(...)" % self.__class__.__name__
__reduce__ = object.__reduce__
class tzstr(tzrange): class tzstr(tzrange):
def __init__(self, s): def __init__(self, s):
global parser
if not parser:
from dateutil import parser
self._s = s self._s = s
res = parser._parsetz(s) res = parser._parsetz(s)
if res is None: if res is None:
raise ValueError, "unknown string format" 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 # We must initialize it first, since _delta() needs
# _std_offset and _dst_offset set. Use False in start/end # _std_offset and _dst_offset set. Use False in start/end
# to avoid building it two times. # to avoid building it two times.
@ -529,6 +575,10 @@ class tzstr(tzrange):
res.dstabbr, res.dstoffset, res.dstabbr, res.dstoffset,
start=False, end=False) start=False, end=False)
if not res.dstabbr:
self._start_delta = None
self._end_delta = None
else:
self._start_delta = self._delta(res.start) self._start_delta = self._delta(res.start)
if self._start_delta: if self._start_delta:
self._end_delta = self._delta(res.end, isend=1) self._end_delta = self._delta(res.end, isend=1)
@ -646,9 +696,10 @@ class _tzicalvtz(datetime.tzinfo):
def __repr__(self): def __repr__(self):
return "<tzicalvtz %s>" % `self._tzid` return "<tzicalvtz %s>" % `self._tzid`
__reduce__ = object.__reduce__
class tzical: class tzical:
def __init__(self, fileobj): def __init__(self, fileobj):
# XXX This should be fixed, but doesn't seem to be in our dateutil.
global rrule global rrule
if not rrule: if not rrule:
from dateutil import rrule from dateutil import rrule
@ -672,9 +723,9 @@ class tzical:
if tzid is None: if tzid is None:
keys = self._vtz.keys() keys = self._vtz.keys()
if len(keys) == 0: if len(keys) == 0:
raise "no timezones defined" raise ValueError, "no timezones defined"
elif len(keys) > 1: elif len(keys) > 1:
raise "more than one timezone available" raise ValueError, "more than one timezone available"
tzid = keys[0] tzid = keys[0]
return self._vtz.get(tzid) return self._vtz.get(tzid)
@ -711,6 +762,8 @@ class tzical:
else: else:
i += 1 i += 1
tzid = None
comps = []
invtz = False invtz = False
comptype = None comptype = None
for line in lines: for line in lines:
@ -753,10 +806,10 @@ class tzical:
if not founddtstart: if not founddtstart:
raise ValueError, \ raise ValueError, \
"mandatory DTSTART not found" "mandatory DTSTART not found"
if not tzoffsetfrom: if tzoffsetfrom is None:
raise ValueError, \ raise ValueError, \
"mandatory TZOFFSETFROM not found" "mandatory TZOFFSETFROM not found"
if not tzoffsetto: if tzoffsetto is None:
raise ValueError, \ raise ValueError, \
"mandatory TZOFFSETFROM not found" "mandatory TZOFFSETFROM not found"
# Process component # Process component
@ -817,10 +870,12 @@ class tzical:
def __repr__(self): def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, `self._s`) return "%s(%s)" % (self.__class__.__name__, `self._s`)
if sys.platform != "win32":
TZFILES = ["/etc/localtime", "localtime"] TZFILES = ["/etc/localtime", "localtime"]
TZPATHS = ["/usr/share/zoneinfo", "/usr/lib/zoneinfo", "/etc/zoneinfo"] TZPATHS = ["/usr/share/zoneinfo", "/usr/lib/zoneinfo", "/etc/zoneinfo"]
else:
import os TZFILES = []
TZPATHS = []
def gettz(name=None): def gettz(name=None):
tz = None tz = None
@ -829,7 +884,7 @@ def gettz(name=None):
name = os.environ["TZ"] name = os.environ["TZ"]
except KeyError: except KeyError:
pass pass
if name is None: if name is None or name == ":":
for filepath in TZFILES: for filepath in TZFILES:
if not os.path.isabs(filepath): if not os.path.isabs(filepath):
filename = filepath filename = filepath
@ -846,8 +901,16 @@ def gettz(name=None):
except (IOError, OSError, ValueError): except (IOError, OSError, ValueError):
pass pass
else: else:
if name and name[0] == ":": tz = tzlocal()
else:
if name.startswith(":"):
name = name[:-1] name = name[:-1]
if os.path.isabs(name):
if os.path.isfile(name):
tz = tzfile(name)
else:
tz = None
else:
for path in TZPATHS: for path in TZPATHS:
filepath = os.path.join(path, name) filepath = os.path.join(path, name)
if not os.path.isfile(filepath): if not os.path.isfile(filepath):
@ -860,6 +923,16 @@ def gettz(name=None):
except (IOError, OSError, ValueError): except (IOError, OSError, ValueError):
pass pass
else: 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: for c in name:
# name must have at least one offset to be a tzstr # name must have at least one offset to be a tzstr
if c in "0123456789": if c in "0123456789":

View File

@ -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

View File

@ -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)