mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 17:37:37 -02:30
Upgrade python dateutil to 2.4.0
This commit is contained in:
@@ -45,7 +45,7 @@ pexpect==3.1 (pexpect/*, excluded pxssh.py, fdpexpect.py, FSM.py, screen.py,
|
|||||||
pip==1.5.4 (pip/*, excluded bin/pip*)
|
pip==1.5.4 (pip/*, excluded bin/pip*)
|
||||||
prettytable==0.7.2 (prettytable.py)
|
prettytable==0.7.2 (prettytable.py)
|
||||||
pyrax==1.9.0 (pyrax/*)
|
pyrax==1.9.0 (pyrax/*)
|
||||||
python-dateutil==2.2 (dateutil/*)
|
python-dateutil==2.4.0 (dateutil/*)
|
||||||
python-novaclient==2.18.1 (novaclient/*, excluded bin/nova)
|
python-novaclient==2.18.1 (novaclient/*, excluded bin/nova)
|
||||||
python-swiftclient==2.2.0 (swiftclient/*, excluded bin/swift)
|
python-swiftclient==2.2.0 (swiftclient/*, excluded bin/swift)
|
||||||
pytz==2014.10 (pytz/*)
|
pytz==2014.10 (pytz/*)
|
||||||
|
|||||||
@@ -1,10 +1,2 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
__version__ = "2.4.0"
|
||||||
Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net>
|
|
||||||
|
|
||||||
This module offers extensions to the standard Python
|
|
||||||
datetime module.
|
|
||||||
"""
|
|
||||||
__author__ = "Tomi Pieviläinen <tomi.pievilainen@iki.fi>"
|
|
||||||
__license__ = "Simplified BSD"
|
|
||||||
__version__ = "2.2"
|
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
|
This module offers a generic easter computing method for any given year, using
|
||||||
|
Western, Orthodox or Julian algorithms.
|
||||||
This module offers extensions to the standard Python
|
|
||||||
datetime module.
|
|
||||||
"""
|
"""
|
||||||
__license__ = "Simplified BSD"
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"]
|
__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"]
|
||||||
|
|
||||||
EASTER_JULIAN = 1
|
EASTER_JULIAN = 1
|
||||||
EASTER_ORTHODOX = 2
|
EASTER_ORTHODOX = 2
|
||||||
EASTER_WESTERN = 3
|
EASTER_WESTERN = 3
|
||||||
|
|
||||||
|
|
||||||
def easter(year, method=EASTER_WESTERN):
|
def easter(year, method=EASTER_WESTERN):
|
||||||
"""
|
"""
|
||||||
@@ -24,7 +23,7 @@ def easter(year, method=EASTER_WESTERN):
|
|||||||
|
|
||||||
This algorithm implements three different easter
|
This algorithm implements three different easter
|
||||||
calculation methods:
|
calculation methods:
|
||||||
|
|
||||||
1 - Original calculation in Julian calendar, valid in
|
1 - Original calculation in Julian calendar, valid in
|
||||||
dates after 326 AD
|
dates after 326 AD
|
||||||
2 - Original method, with date converted to Gregorian
|
2 - Original method, with date converted to Gregorian
|
||||||
@@ -39,7 +38,7 @@ def easter(year, method=EASTER_WESTERN):
|
|||||||
EASTER_WESTERN = 3
|
EASTER_WESTERN = 3
|
||||||
|
|
||||||
The default method is method 3.
|
The default method is method 3.
|
||||||
|
|
||||||
More about the algorithm may be found at:
|
More about the algorithm may be found at:
|
||||||
|
|
||||||
http://users.chariot.net.au/~gmarts/eastalg.htm
|
http://users.chariot.net.au/~gmarts/eastalg.htm
|
||||||
@@ -68,24 +67,23 @@ def easter(year, method=EASTER_WESTERN):
|
|||||||
e = 0
|
e = 0
|
||||||
if method < 3:
|
if method < 3:
|
||||||
# Old method
|
# Old method
|
||||||
i = (19*g+15)%30
|
i = (19*g + 15) % 30
|
||||||
j = (y+y//4+i)%7
|
j = (y + y//4 + i) % 7
|
||||||
if method == 2:
|
if method == 2:
|
||||||
# Extra dates to convert Julian to Gregorian date
|
# Extra dates to convert Julian to Gregorian date
|
||||||
e = 10
|
e = 10
|
||||||
if y > 1600:
|
if y > 1600:
|
||||||
e = e+y//100-16-(y//100-16)//4
|
e = e + y//100 - 16 - (y//100 - 16)//4
|
||||||
else:
|
else:
|
||||||
# New method
|
# New method
|
||||||
c = y//100
|
c = y//100
|
||||||
h = (c-c//4-(8*c+13)//25+19*g+15)%30
|
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))
|
i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11))
|
||||||
j = (y+y//4+i+2-c+c//4)%7
|
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
|
# 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)
|
# (later dates apply to method 2, although 23 May never actually occurs)
|
||||||
p = i-j+e
|
p = i - j + e
|
||||||
d = 1+(p+27+(p+6)//40)%31
|
d = 1 + (p + 27 + (p + 6)//40) % 31
|
||||||
m = 3+(p+26)//30
|
m = 3 + (p + 26)//30
|
||||||
return datetime.date(int(y), int(m), int(d))
|
return datetime.date(int(y), int(m), int(d))
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,21 @@
|
|||||||
# -*- coding:iso-8859-1 -*-
|
# -*- coding:iso-8859-1 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
|
This module offers a generic date/time string parser which is able to parse
|
||||||
|
most known formats to represent a date and/or time.
|
||||||
This module offers extensions to the standard Python
|
|
||||||
datetime module.
|
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
__license__ = "Simplified BSD"
|
|
||||||
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import string
|
import string
|
||||||
import time
|
import time
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import collections
|
import collections
|
||||||
|
from io import StringIO
|
||||||
try:
|
|
||||||
from io import StringIO
|
|
||||||
except ImportError:
|
|
||||||
from io import StringIO
|
|
||||||
|
|
||||||
from six import text_type, binary_type, integer_types
|
from six import text_type, binary_type, integer_types
|
||||||
|
|
||||||
from . import relativedelta
|
from . import relativedelta
|
||||||
from . import tz
|
from . import tz
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["parse", "parserinfo"]
|
__all__ = ["parse", "parserinfo"]
|
||||||
|
|
||||||
|
|
||||||
@@ -83,9 +72,9 @@ class _timelex(object):
|
|||||||
state = '0'
|
state = '0'
|
||||||
elif nextchar in whitespace:
|
elif nextchar in whitespace:
|
||||||
token = ' '
|
token = ' '
|
||||||
break # emit token
|
break # emit token
|
||||||
else:
|
else:
|
||||||
break # emit token
|
break # emit token
|
||||||
elif state == 'a':
|
elif state == 'a':
|
||||||
seenletters = True
|
seenletters = True
|
||||||
if nextchar in wordchars:
|
if nextchar in wordchars:
|
||||||
@@ -95,7 +84,7 @@ class _timelex(object):
|
|||||||
state = 'a.'
|
state = 'a.'
|
||||||
else:
|
else:
|
||||||
self.charstack.append(nextchar)
|
self.charstack.append(nextchar)
|
||||||
break # emit token
|
break # emit token
|
||||||
elif state == '0':
|
elif state == '0':
|
||||||
if nextchar in numchars:
|
if nextchar in numchars:
|
||||||
token += nextchar
|
token += nextchar
|
||||||
@@ -104,7 +93,7 @@ class _timelex(object):
|
|||||||
state = '0.'
|
state = '0.'
|
||||||
else:
|
else:
|
||||||
self.charstack.append(nextchar)
|
self.charstack.append(nextchar)
|
||||||
break # emit token
|
break # emit token
|
||||||
elif state == 'a.':
|
elif state == 'a.':
|
||||||
seenletters = True
|
seenletters = True
|
||||||
if nextchar == '.' or nextchar in wordchars:
|
if nextchar == '.' or nextchar in wordchars:
|
||||||
@@ -114,7 +103,7 @@ class _timelex(object):
|
|||||||
state = '0.'
|
state = '0.'
|
||||||
else:
|
else:
|
||||||
self.charstack.append(nextchar)
|
self.charstack.append(nextchar)
|
||||||
break # emit token
|
break # emit token
|
||||||
elif state == '0.':
|
elif state == '0.':
|
||||||
if nextchar == '.' or nextchar in numchars:
|
if nextchar == '.' or nextchar in numchars:
|
||||||
token += nextchar
|
token += nextchar
|
||||||
@@ -123,9 +112,9 @@ class _timelex(object):
|
|||||||
state = 'a.'
|
state = 'a.'
|
||||||
else:
|
else:
|
||||||
self.charstack.append(nextchar)
|
self.charstack.append(nextchar)
|
||||||
break # emit token
|
break # emit token
|
||||||
if (state in ('a.', '0.') and
|
if (state in ('a.', '0.') and (seenletters or token.count('.') > 1 or
|
||||||
(seenletters or token.count('.') > 1 or token[-1] == '.')):
|
token[-1] == '.')):
|
||||||
l = token.split('.')
|
l = token.split('.')
|
||||||
token = l[0]
|
token = l[0]
|
||||||
for tok in l[1:]:
|
for tok in l[1:]:
|
||||||
@@ -183,18 +172,18 @@ class parserinfo(object):
|
|||||||
("Fri", "Friday"),
|
("Fri", "Friday"),
|
||||||
("Sat", "Saturday"),
|
("Sat", "Saturday"),
|
||||||
("Sun", "Sunday")]
|
("Sun", "Sunday")]
|
||||||
MONTHS = [("Jan", "January"),
|
MONTHS = [("Jan", "January"),
|
||||||
("Feb", "February"),
|
("Feb", "February"),
|
||||||
("Mar", "March"),
|
("Mar", "March"),
|
||||||
("Apr", "April"),
|
("Apr", "April"),
|
||||||
("May", "May"),
|
("May", "May"),
|
||||||
("Jun", "June"),
|
("Jun", "June"),
|
||||||
("Jul", "July"),
|
("Jul", "July"),
|
||||||
("Aug", "August"),
|
("Aug", "August"),
|
||||||
("Sep", "Sept", "September"),
|
("Sep", "Sept", "September"),
|
||||||
("Oct", "October"),
|
("Oct", "October"),
|
||||||
("Nov", "November"),
|
("Nov", "November"),
|
||||||
("Dec", "December")]
|
("Dec", "December")]
|
||||||
HMS = [("h", "hour", "hours"),
|
HMS = [("h", "hour", "hours"),
|
||||||
("m", "minute", "minutes"),
|
("m", "minute", "minutes"),
|
||||||
("s", "second", "seconds")]
|
("s", "second", "seconds")]
|
||||||
@@ -299,15 +288,16 @@ class parser(object):
|
|||||||
def __init__(self, info=None):
|
def __init__(self, info=None):
|
||||||
self.info = info or 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,
|
**kwargs):
|
||||||
**kwargs):
|
|
||||||
if not default:
|
if not default:
|
||||||
default = datetime.datetime.now().replace(hour=0, minute=0,
|
default = datetime.datetime.now().replace(hour=0, minute=0,
|
||||||
second=0, microsecond=0)
|
second=0, microsecond=0)
|
||||||
|
|
||||||
|
if kwargs.get('fuzzy_with_tokens', False):
|
||||||
res, skipped_tokens = self._parse(timestr, **kwargs)
|
res, skipped_tokens = self._parse(timestr, **kwargs)
|
||||||
|
else:
|
||||||
|
res = self._parse(timestr, **kwargs)
|
||||||
|
|
||||||
if res is None:
|
if res is None:
|
||||||
raise ValueError("unknown string format")
|
raise ValueError("unknown string format")
|
||||||
@@ -321,7 +311,8 @@ class parser(object):
|
|||||||
if res.weekday is not None and not res.day:
|
if res.weekday is not None and not res.day:
|
||||||
ret = ret+relativedelta.relativedelta(weekday=res.weekday)
|
ret = ret+relativedelta.relativedelta(weekday=res.weekday)
|
||||||
if not ignoretz:
|
if not ignoretz:
|
||||||
if isinstance(tzinfos, collections.Callable) or tzinfos and res.tzname in tzinfos:
|
if (isinstance(tzinfos, collections.Callable) or
|
||||||
|
tzinfos and res.tzname in tzinfos):
|
||||||
if isinstance(tzinfos, collections.Callable):
|
if isinstance(tzinfos, collections.Callable):
|
||||||
tzdata = tzinfos(res.tzname, res.tzoffset)
|
tzdata = tzinfos(res.tzname, res.tzoffset)
|
||||||
else:
|
else:
|
||||||
@@ -333,8 +324,8 @@ class parser(object):
|
|||||||
elif isinstance(tzdata, integer_types):
|
elif isinstance(tzdata, integer_types):
|
||||||
tzinfo = tz.tzoffset(res.tzname, tzdata)
|
tzinfo = tz.tzoffset(res.tzname, tzdata)
|
||||||
else:
|
else:
|
||||||
raise ValueError("offset must be tzinfo subclass, " \
|
raise ValueError("offset must be tzinfo subclass, "
|
||||||
"tz string, or int offset")
|
"tz string, or int offset")
|
||||||
ret = ret.replace(tzinfo=tzinfo)
|
ret = ret.replace(tzinfo=tzinfo)
|
||||||
elif res.tzname and res.tzname in time.tzname:
|
elif res.tzname and res.tzname in time.tzname:
|
||||||
ret = ret.replace(tzinfo=tz.tzlocal())
|
ret = ret.replace(tzinfo=tz.tzlocal())
|
||||||
@@ -343,17 +334,18 @@ class parser(object):
|
|||||||
elif res.tzoffset:
|
elif res.tzoffset:
|
||||||
ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset))
|
ret = ret.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset))
|
||||||
|
|
||||||
if skipped_tokens:
|
if kwargs.get('fuzzy_with_tokens', False):
|
||||||
return ret, skipped_tokens
|
return ret, skipped_tokens
|
||||||
|
else:
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
class _result(_resultbase):
|
class _result(_resultbase):
|
||||||
__slots__ = ["year", "month", "day", "weekday",
|
__slots__ = ["year", "month", "day", "weekday",
|
||||||
"hour", "minute", "second", "microsecond",
|
"hour", "minute", "second", "microsecond",
|
||||||
"tzname", "tzoffset"]
|
"tzname", "tzoffset"]
|
||||||
|
|
||||||
def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, fuzzy_with_tokens=False):
|
def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False,
|
||||||
|
fuzzy_with_tokens=False):
|
||||||
if fuzzy_with_tokens:
|
if fuzzy_with_tokens:
|
||||||
fuzzy = True
|
fuzzy = True
|
||||||
|
|
||||||
@@ -365,7 +357,6 @@ class parser(object):
|
|||||||
res = self._result()
|
res = self._result()
|
||||||
l = _timelex.split(timestr)
|
l = _timelex.split(timestr)
|
||||||
|
|
||||||
|
|
||||||
# keep up with the last token skipped so we can recombine
|
# keep up with the last token skipped so we can recombine
|
||||||
# consecutively skipped tokens (-2 for when i begins at 0).
|
# consecutively skipped tokens (-2 for when i begins at 0).
|
||||||
last_skipped_token_i = -2
|
last_skipped_token_i = -2
|
||||||
@@ -440,12 +431,12 @@ class parser(object):
|
|||||||
while True:
|
while True:
|
||||||
if idx == 0:
|
if idx == 0:
|
||||||
res.hour = int(value)
|
res.hour = int(value)
|
||||||
if value%1:
|
if value % 1:
|
||||||
res.minute = int(60*(value%1))
|
res.minute = int(60*(value % 1))
|
||||||
elif idx == 1:
|
elif idx == 1:
|
||||||
res.minute = int(value)
|
res.minute = int(value)
|
||||||
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, res.microsecond = \
|
res.second, res.microsecond = \
|
||||||
_parsems(value_repr)
|
_parsems(value_repr)
|
||||||
@@ -465,16 +456,17 @@ class parser(object):
|
|||||||
newidx = info.hms(l[i])
|
newidx = info.hms(l[i])
|
||||||
if newidx is not None:
|
if newidx is not None:
|
||||||
idx = newidx
|
idx = newidx
|
||||||
elif i == len_l and l[i-2] == ' ' and info.hms(l[i-3]) is not None:
|
elif (i == len_l and l[i-2] == ' ' and
|
||||||
|
info.hms(l[i-3]) is not None):
|
||||||
# X h MM or X m SS
|
# X h MM or X m SS
|
||||||
idx = info.hms(l[i-3]) + 1
|
idx = info.hms(l[i-3]) + 1
|
||||||
if idx == 1:
|
if idx == 1:
|
||||||
res.minute = int(value)
|
res.minute = int(value)
|
||||||
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, res.microsecond = \
|
res.second, res.microsecond = \
|
||||||
_parsems(value_repr)
|
_parsems(value_repr)
|
||||||
i += 1
|
i += 1
|
||||||
elif i+1 < len_l and l[i] == ':':
|
elif i+1 < len_l and l[i] == ':':
|
||||||
# HH:MM[:SS[.ss]]
|
# HH:MM[:SS[.ss]]
|
||||||
@@ -482,8 +474,8 @@ class parser(object):
|
|||||||
i += 1
|
i += 1
|
||||||
value = float(l[i])
|
value = float(l[i])
|
||||||
res.minute = int(value)
|
res.minute = int(value)
|
||||||
if value%1:
|
if value % 1:
|
||||||
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] == ':':
|
||||||
res.second, res.microsecond = _parsems(l[i+1])
|
res.second, res.microsecond = _parsems(l[i+1])
|
||||||
@@ -597,8 +589,9 @@ class parser(object):
|
|||||||
|
|
||||||
# Check for a timezone name
|
# Check for a timezone name
|
||||||
if (res.hour is not None and len(l[i]) <= 5 and
|
if (res.hour is not None and len(l[i]) <= 5 and
|
||||||
res.tzname is None and res.tzoffset is None 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]):
|
not [x for x in l[i] if x not in
|
||||||
|
string.ascii_uppercase]):
|
||||||
res.tzname = l[i]
|
res.tzname = l[i]
|
||||||
res.tzoffset = info.tzoffset(res.tzname)
|
res.tzoffset = info.tzoffset(res.tzname)
|
||||||
i += 1
|
i += 1
|
||||||
@@ -643,7 +636,7 @@ class parser(object):
|
|||||||
info.jump(l[i]) and l[i+1] == '(' and l[i+3] == ')' and
|
info.jump(l[i]) and l[i+1] == '(' and l[i+3] == ')' and
|
||||||
3 <= len(l[i+2]) <= 5 and
|
3 <= len(l[i+2]) <= 5 and
|
||||||
not [x for x in l[i+2]
|
not [x for x in l[i+2]
|
||||||
if x not in string.ascii_uppercase]):
|
if x not in string.ascii_uppercase]):
|
||||||
# -0300 (BRST)
|
# -0300 (BRST)
|
||||||
res.tzname = l[i+2]
|
res.tzname = l[i+2]
|
||||||
i += 4
|
i += 4
|
||||||
@@ -732,10 +725,12 @@ class parser(object):
|
|||||||
|
|
||||||
if fuzzy_with_tokens:
|
if fuzzy_with_tokens:
|
||||||
return res, tuple(skipped_tokens)
|
return res, tuple(skipped_tokens)
|
||||||
|
else:
|
||||||
return res, None
|
return res
|
||||||
|
|
||||||
DEFAULTPARSER = parser()
|
DEFAULTPARSER = parser()
|
||||||
|
|
||||||
|
|
||||||
def parse(timestr, parserinfo=None, **kwargs):
|
def parse(timestr, parserinfo=None, **kwargs):
|
||||||
# Python 2.x support: datetimes return their string presentation as
|
# Python 2.x support: datetimes return their string presentation as
|
||||||
# bytes in 2.x and unicode in 3.x, so it's reasonable to expect that
|
# bytes in 2.x and unicode in 3.x, so it's reasonable to expect that
|
||||||
@@ -779,7 +774,7 @@ class _tzparser(object):
|
|||||||
# BRST+3[BRDT[+2]]
|
# BRST+3[BRDT[+2]]
|
||||||
j = i
|
j = i
|
||||||
while j < len_l and not [x for x in l[j]
|
while j < len_l and not [x for x in l[j]
|
||||||
if x in "0123456789:,-+"]:
|
if x in "0123456789:,-+"]:
|
||||||
j += 1
|
j += 1
|
||||||
if j != i:
|
if j != i:
|
||||||
if not res.stdabbr:
|
if not res.stdabbr:
|
||||||
@@ -789,8 +784,8 @@ class _tzparser(object):
|
|||||||
offattr = "dstoffset"
|
offattr = "dstoffset"
|
||||||
res.dstabbr = "".join(l[i:j])
|
res.dstabbr = "".join(l[i:j])
|
||||||
i = j
|
i = j
|
||||||
if (i < len_l and
|
if (i < len_l and (l[i] in ('+', '-') or l[i][0] in
|
||||||
(l[i] in ('+', '-') or l[i][0] in "0123456789")):
|
"0123456789")):
|
||||||
if l[i] in ('+', '-'):
|
if l[i] in ('+', '-'):
|
||||||
# Yes, that's right. See the TZ variable
|
# Yes, that's right. See the TZ variable
|
||||||
# documentation.
|
# documentation.
|
||||||
@@ -801,8 +796,8 @@ class _tzparser(object):
|
|||||||
len_li = len(l[i])
|
len_li = len(l[i])
|
||||||
if len_li == 4:
|
if len_li == 4:
|
||||||
# -0300
|
# -0300
|
||||||
setattr(res, offattr,
|
setattr(res, offattr, (int(l[i][:2])*3600 +
|
||||||
(int(l[i][:2])*3600+int(l[i][2:])*60)*signal)
|
int(l[i][2:])*60)*signal)
|
||||||
elif i+1 < len_l and l[i+1] == ':':
|
elif i+1 < len_l and l[i+1] == ':':
|
||||||
# -03:00
|
# -03:00
|
||||||
setattr(res, offattr,
|
setattr(res, offattr,
|
||||||
@@ -822,7 +817,8 @@ class _tzparser(object):
|
|||||||
|
|
||||||
if i < len_l:
|
if i < len_l:
|
||||||
for j in range(i, len_l):
|
for j in range(i, len_l):
|
||||||
if l[j] == ';': l[j] = ','
|
if l[j] == ';':
|
||||||
|
l[j] = ','
|
||||||
|
|
||||||
assert l[i] == ','
|
assert l[i] == ','
|
||||||
|
|
||||||
@@ -831,7 +827,7 @@ class _tzparser(object):
|
|||||||
if i >= len_l:
|
if i >= len_l:
|
||||||
pass
|
pass
|
||||||
elif (8 <= l.count(',') <= 9 and
|
elif (8 <= l.count(',') <= 9 and
|
||||||
not [y for x in l[i:] if x != ','
|
not [y for x in l[i:] if x != ','
|
||||||
for y in x if y not in "0123456789"]):
|
for y in x if y not in "0123456789"]):
|
||||||
# GMT0BST,3,0,30,3600,10,0,26,7200[,3600]
|
# GMT0BST,3,0,30,3600,10,0,26,7200[,3600]
|
||||||
for x in (res.start, res.end):
|
for x in (res.start, res.end):
|
||||||
@@ -845,7 +841,7 @@ class _tzparser(object):
|
|||||||
i += 2
|
i += 2
|
||||||
if value:
|
if value:
|
||||||
x.week = value
|
x.week = value
|
||||||
x.weekday = (int(l[i])-1)%7
|
x.weekday = (int(l[i])-1) % 7
|
||||||
else:
|
else:
|
||||||
x.day = int(l[i])
|
x.day = int(l[i])
|
||||||
i += 2
|
i += 2
|
||||||
@@ -861,7 +857,7 @@ class _tzparser(object):
|
|||||||
elif (l.count(',') == 2 and l[i:].count('/') <= 2 and
|
elif (l.count(',') == 2 and l[i:].count('/') <= 2 and
|
||||||
not [y for x in l[i:] if x not in (',', '/', 'J', 'M',
|
not [y for x in l[i:] if x not in (',', '/', 'J', 'M',
|
||||||
'.', '-', ':')
|
'.', '-', ':')
|
||||||
for y in x if y not in "0123456789"]):
|
for y in x if y not in "0123456789"]):
|
||||||
for x in (res.start, res.end):
|
for x in (res.start, res.end):
|
||||||
if l[i] == 'J':
|
if l[i] == 'J':
|
||||||
# non-leap year day (1 based)
|
# non-leap year day (1 based)
|
||||||
@@ -880,7 +876,7 @@ class _tzparser(object):
|
|||||||
i += 1
|
i += 1
|
||||||
assert l[i] in ('-', '.')
|
assert l[i] in ('-', '.')
|
||||||
i += 1
|
i += 1
|
||||||
x.weekday = (int(l[i])-1)%7
|
x.weekday = (int(l[i])-1) % 7
|
||||||
else:
|
else:
|
||||||
# year day (zero based)
|
# year day (zero based)
|
||||||
x.yday = int(l[i])+1
|
x.yday = int(l[i])+1
|
||||||
@@ -921,6 +917,8 @@ class _tzparser(object):
|
|||||||
|
|
||||||
|
|
||||||
DEFAULTTZPARSER = _tzparser()
|
DEFAULTTZPARSER = _tzparser()
|
||||||
|
|
||||||
|
|
||||||
def _parsetz(tzstr):
|
def _parsetz(tzstr):
|
||||||
return DEFAULTTZPARSER.parse(tzstr)
|
return DEFAULTTZPARSER.parse(tzstr)
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
"""
|
# -*- coding: utf-8 -*-
|
||||||
Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net>
|
|
||||||
|
|
||||||
This module offers extensions to the standard Python
|
|
||||||
datetime module.
|
|
||||||
"""
|
|
||||||
__license__ = "Simplified BSD"
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import calendar
|
import calendar
|
||||||
|
|
||||||
@@ -13,6 +6,7 @@ from six import integer_types
|
|||||||
|
|
||||||
__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
|
__all__ = ["relativedelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
|
||||||
|
|
||||||
|
|
||||||
class weekday(object):
|
class weekday(object):
|
||||||
__slots__ = ["weekday", "n"]
|
__slots__ = ["weekday", "n"]
|
||||||
|
|
||||||
@@ -43,25 +37,35 @@ class weekday(object):
|
|||||||
|
|
||||||
MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)])
|
MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)])
|
||||||
|
|
||||||
|
|
||||||
class relativedelta(object):
|
class relativedelta(object):
|
||||||
"""
|
"""
|
||||||
The relativedelta type is based on the specification of the excelent
|
The relativedelta type is based on the specification of the excellent
|
||||||
work done by M.-A. Lemburg in his mx.DateTime extension. However,
|
work done by M.-A. Lemburg in his
|
||||||
notice that this type does *NOT* implement the same algorithm as
|
`mx.DateTime <http://www.egenix.com/files/python/mxDateTime.html>`_ 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.
|
his work. Do *NOT* expect it to behave like mx.DateTime's counterpart.
|
||||||
|
|
||||||
There's two different ways to build a relativedelta instance. The
|
There are two different ways to build a relativedelta instance. The
|
||||||
first one is passing it two date/datetime classes:
|
first one is passing it two date/datetime classes::
|
||||||
|
|
||||||
relativedelta(datetime1, datetime2)
|
relativedelta(datetime1, datetime2)
|
||||||
|
|
||||||
And the other way is to use the following keyword arguments:
|
The second one is passing it any number of the following keyword arguments::
|
||||||
|
|
||||||
|
relativedelta(arg1=x,arg2=y,arg3=z...)
|
||||||
|
|
||||||
year, month, day, hour, minute, second, microsecond:
|
year, month, day, hour, minute, second, microsecond:
|
||||||
Absolute information.
|
Absolute information (argument is singular); adding or subtracting a
|
||||||
|
relativedelta with absolute information does not perform an aritmetic
|
||||||
|
operation, but rather REPLACES the corresponding value in the
|
||||||
|
original datetime with the value(s) in relativedelta.
|
||||||
|
|
||||||
years, months, weeks, days, hours, minutes, seconds, microseconds:
|
years, months, weeks, days, hours, minutes, seconds, microseconds:
|
||||||
Relative information, may be negative.
|
Relative information, may be negative (argument is plural); adding
|
||||||
|
or subtracting a relativedelta with relative information performs
|
||||||
|
the corresponding aritmetic operation on the original datetime value
|
||||||
|
with the information in the relativedelta.
|
||||||
|
|
||||||
weekday:
|
weekday:
|
||||||
One of the weekday instances (MO, TU, etc). These instances may
|
One of the weekday instances (MO, TU, etc). These instances may
|
||||||
@@ -80,26 +84,26 @@ And the other way is to use the following keyword arguments:
|
|||||||
|
|
||||||
Here is the behavior of operations with relativedelta:
|
Here is the behavior of operations with relativedelta:
|
||||||
|
|
||||||
1) Calculate the absolute year, using the 'year' argument, or the
|
1. Calculate the absolute year, using the 'year' argument, or the
|
||||||
original datetime year, if the argument is not present.
|
original datetime year, if the argument is not present.
|
||||||
|
|
||||||
2) Add the relative 'years' argument to the absolute year.
|
2. Add the relative 'years' argument to the absolute year.
|
||||||
|
|
||||||
3) Do steps 1 and 2 for month/months.
|
3. Do steps 1 and 2 for month/months.
|
||||||
|
|
||||||
4) Calculate the absolute day, using the 'day' argument, or the
|
4. Calculate the absolute day, using the 'day' argument, or the
|
||||||
original datetime day, if the argument is not present. Then,
|
original datetime day, if the argument is not present. Then,
|
||||||
subtract from the day until it fits in the year and month
|
subtract from the day until it fits in the year and month
|
||||||
found after their operations.
|
found after their operations.
|
||||||
|
|
||||||
5) Add the relative 'days' argument to the absolute day. Notice
|
5. Add the relative 'days' argument to the absolute day. Notice
|
||||||
that the 'weeks' argument is multiplied by 7 and added to
|
that the 'weeks' argument is multiplied by 7 and added to
|
||||||
'days'.
|
'days'.
|
||||||
|
|
||||||
6) Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds,
|
6. Do steps 1 and 2 for hour/hours, minute/minutes, second/seconds,
|
||||||
microsecond/microseconds.
|
microsecond/microseconds.
|
||||||
|
|
||||||
7) If the 'weekday' argument is present, calculate the weekday,
|
7. If the 'weekday' argument is present, calculate the weekday,
|
||||||
with the given (wday, nth) tuple. wday is the index of the
|
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
|
weekday (0-6, 0=Mon), and nth is the number of weeks to add
|
||||||
forward or backward, depending on its signal. Notice that if
|
forward or backward, depending on its signal. Notice that if
|
||||||
@@ -114,9 +118,14 @@ Here is the behavior of operations with relativedelta:
|
|||||||
yearday=None, nlyearday=None,
|
yearday=None, nlyearday=None,
|
||||||
hour=None, minute=None, second=None, microsecond=None):
|
hour=None, minute=None, second=None, microsecond=None):
|
||||||
if dt1 and dt2:
|
if dt1 and dt2:
|
||||||
if (not isinstance(dt1, datetime.date)) or (not isinstance(dt2, datetime.date)):
|
# datetime is a subclass of date. So both must be date
|
||||||
|
if not (isinstance(dt1, datetime.date) and
|
||||||
|
isinstance(dt2, datetime.date)):
|
||||||
raise TypeError("relativedelta only diffs datetime/date")
|
raise TypeError("relativedelta only diffs datetime/date")
|
||||||
if not type(dt1) == type(dt2): #isinstance(dt1, type(dt2)):
|
# We allow two dates, or two datetimes, so we coerce them to be
|
||||||
|
# of the same type
|
||||||
|
if (isinstance(dt1, datetime.datetime) !=
|
||||||
|
isinstance(dt2, datetime.datetime)):
|
||||||
if not isinstance(dt1, datetime.datetime):
|
if not isinstance(dt1, datetime.datetime):
|
||||||
dt1 = datetime.datetime.fromordinal(dt1.toordinal())
|
dt1 = datetime.datetime.fromordinal(dt1.toordinal())
|
||||||
elif not isinstance(dt2, datetime.datetime):
|
elif not isinstance(dt2, datetime.datetime):
|
||||||
@@ -185,7 +194,8 @@ Here is the behavior of operations with relativedelta:
|
|||||||
if yearday > 59:
|
if yearday > 59:
|
||||||
self.leapdays = -1
|
self.leapdays = -1
|
||||||
if yday:
|
if yday:
|
||||||
ydayidx = [31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 366]
|
ydayidx = [31, 59, 90, 120, 151, 181, 212,
|
||||||
|
243, 273, 304, 334, 366]
|
||||||
for idx, ydays in enumerate(ydayidx):
|
for idx, ydays in enumerate(ydayidx):
|
||||||
if yday <= ydays:
|
if yday <= ydays:
|
||||||
self.month = idx+1
|
self.month = idx+1
|
||||||
@@ -225,9 +235,9 @@ Here is the behavior of operations with relativedelta:
|
|||||||
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
|
||||||
if (self.hours or self.minutes or self.seconds or self.microseconds or
|
if (self.hours or self.minutes or self.seconds or self.microseconds
|
||||||
self.hour is not None or self.minute is not None or
|
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.second is not None or self.microsecond is not None):
|
||||||
self._has_time = 1
|
self._has_time = 1
|
||||||
else:
|
else:
|
||||||
self._has_time = 0
|
self._has_time = 0
|
||||||
@@ -245,21 +255,23 @@ Here is the behavior of operations with relativedelta:
|
|||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
if isinstance(other, relativedelta):
|
if isinstance(other, relativedelta):
|
||||||
return relativedelta(years=other.years+self.years,
|
return relativedelta(years=other.years+self.years,
|
||||||
months=other.months+self.months,
|
months=other.months+self.months,
|
||||||
days=other.days+self.days,
|
days=other.days+self.days,
|
||||||
hours=other.hours+self.hours,
|
hours=other.hours+self.hours,
|
||||||
minutes=other.minutes+self.minutes,
|
minutes=other.minutes+self.minutes,
|
||||||
seconds=other.seconds+self.seconds,
|
seconds=other.seconds+self.seconds,
|
||||||
microseconds=other.microseconds+self.microseconds,
|
microseconds=(other.microseconds +
|
||||||
leapdays=other.leapdays or self.leapdays,
|
self.microseconds),
|
||||||
year=other.year or self.year,
|
leapdays=other.leapdays or self.leapdays,
|
||||||
month=other.month or self.month,
|
year=other.year or self.year,
|
||||||
day=other.day or self.day,
|
month=other.month or self.month,
|
||||||
weekday=other.weekday or self.weekday,
|
day=other.day or self.day,
|
||||||
hour=other.hour or self.hour,
|
weekday=other.weekday or self.weekday,
|
||||||
minute=other.minute or self.minute,
|
hour=other.hour or self.hour,
|
||||||
second=other.second or self.second,
|
minute=other.minute or self.minute,
|
||||||
microsecond=other.microsecond or self.microsecond)
|
second=other.second or self.second,
|
||||||
|
microsecond=(other.microsecond or
|
||||||
|
self.microsecond))
|
||||||
if not isinstance(other, datetime.date):
|
if not isinstance(other, datetime.date):
|
||||||
raise TypeError("unsupported type for add operation")
|
raise TypeError("unsupported type for add operation")
|
||||||
elif self._has_time and not isinstance(other, datetime.datetime):
|
elif self._has_time and not isinstance(other, datetime.datetime):
|
||||||
@@ -295,9 +307,9 @@ Here is the behavior of operations with relativedelta:
|
|||||||
weekday, nth = self.weekday.weekday, self.weekday.n or 1
|
weekday, nth = self.weekday.weekday, self.weekday.n or 1
|
||||||
jumpdays = (abs(nth)-1)*7
|
jumpdays = (abs(nth)-1)*7
|
||||||
if nth > 0:
|
if nth > 0:
|
||||||
jumpdays += (7-ret.weekday()+weekday)%7
|
jumpdays += (7-ret.weekday()+weekday) % 7
|
||||||
else:
|
else:
|
||||||
jumpdays += (ret.weekday()-weekday)%7
|
jumpdays += (ret.weekday()-weekday) % 7
|
||||||
jumpdays *= -1
|
jumpdays *= -1
|
||||||
ret += datetime.timedelta(days=jumpdays)
|
ret += datetime.timedelta(days=jumpdays)
|
||||||
return ret
|
return ret
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2003-2010 Gustavo Niemeyer <gustavo@niemeyer.net>
|
The rrule module offers a small, complete, and very fast, implementation of
|
||||||
|
the recurrence rules documented in the
|
||||||
This module offers extensions to the standard Python
|
`iCalendar RFC <http://www.ietf.org/rfc/rfc2445.txt>`_,
|
||||||
datetime module.
|
including support for caching of results.
|
||||||
"""
|
"""
|
||||||
__license__ = "Simplified BSD"
|
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
import datetime
|
import datetime
|
||||||
import calendar
|
import calendar
|
||||||
try:
|
|
||||||
import _thread
|
|
||||||
except ImportError:
|
|
||||||
import thread as _thread
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from fractions import gcd
|
||||||
|
|
||||||
from six import advance_iterator, integer_types
|
from six import advance_iterator, integer_types
|
||||||
|
from six.moves import _thread
|
||||||
|
|
||||||
__all__ = ["rrule", "rruleset", "rrulestr",
|
__all__ = ["rrule", "rruleset", "rrulestr",
|
||||||
"YEARLY", "MONTHLY", "WEEKLY", "DAILY",
|
"YEARLY", "MONTHLY", "WEEKLY", "DAILY",
|
||||||
@@ -23,7 +21,7 @@ __all__ = ["rrule", "rruleset", "rrulestr",
|
|||||||
"MO", "TU", "WE", "TH", "FR", "SA", "SU"]
|
"MO", "TU", "WE", "TH", "FR", "SA", "SU"]
|
||||||
|
|
||||||
# Every mask is 7 days longer to handle cross-year weekly periods.
|
# Every mask is 7 days longer to handle cross-year weekly periods.
|
||||||
M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30+
|
M366MASK = tuple([1]*31+[2]*29+[3]*31+[4]*30+[5]*31+[6]*30 +
|
||||||
[7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7)
|
[7]*31+[8]*31+[9]*30+[10]*31+[11]*30+[12]*31+[1]*7)
|
||||||
M365MASK = list(M366MASK)
|
M365MASK = list(M366MASK)
|
||||||
M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32))
|
M29, M30, M31 = list(range(1, 30)), list(range(1, 31)), list(range(1, 32))
|
||||||
@@ -51,6 +49,7 @@ M365MASK = tuple(M365MASK)
|
|||||||
easter = None
|
easter = None
|
||||||
parser = None
|
parser = None
|
||||||
|
|
||||||
|
|
||||||
class weekday(object):
|
class weekday(object):
|
||||||
__slots__ = ["weekday", "n"]
|
__slots__ = ["weekday", "n"]
|
||||||
|
|
||||||
@@ -83,12 +82,13 @@ class weekday(object):
|
|||||||
|
|
||||||
MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)])
|
MO, TU, WE, TH, FR, SA, SU = weekdays = tuple([weekday(x) for x in range(7)])
|
||||||
|
|
||||||
|
|
||||||
class rrulebase(object):
|
class rrulebase(object):
|
||||||
def __init__(self, cache=False):
|
def __init__(self, cache=False):
|
||||||
if cache:
|
if cache:
|
||||||
self._cache = []
|
self._cache = []
|
||||||
self._cache_lock = _thread.allocate_lock()
|
self._cache_lock = _thread.allocate_lock()
|
||||||
self._cache_gen = self._iter()
|
self._cache_gen = self._iter()
|
||||||
self._cache_complete = False
|
self._cache_complete = False
|
||||||
else:
|
else:
|
||||||
self._cache = None
|
self._cache = None
|
||||||
@@ -163,11 +163,17 @@ class rrulebase(object):
|
|||||||
|
|
||||||
# __len__() introduces a large performance penality.
|
# __len__() introduces a large performance penality.
|
||||||
def count(self):
|
def count(self):
|
||||||
|
""" Returns the number of recurrences in this set. It will have go
|
||||||
|
trough the whole recurrence, if this hasn't been done before. """
|
||||||
if self._len is None:
|
if self._len is None:
|
||||||
for x in self: pass
|
for x in self:
|
||||||
|
pass
|
||||||
return self._len
|
return self._len
|
||||||
|
|
||||||
def before(self, dt, inc=False):
|
def before(self, dt, inc=False):
|
||||||
|
""" Returns the last recurrence before the given datetime instance. The
|
||||||
|
inc keyword defines what happens if dt is an occurrence. With
|
||||||
|
inc=True, if dt itself is an occurrence, it will be returned. """
|
||||||
if self._cache_complete:
|
if self._cache_complete:
|
||||||
gen = self._cache
|
gen = self._cache
|
||||||
else:
|
else:
|
||||||
@@ -186,6 +192,9 @@ class rrulebase(object):
|
|||||||
return last
|
return last
|
||||||
|
|
||||||
def after(self, dt, inc=False):
|
def after(self, dt, inc=False):
|
||||||
|
""" Returns the first recurrence after the given datetime instance. The
|
||||||
|
inc keyword defines what happens if dt is an occurrence. With
|
||||||
|
inc=True, if dt itself is an occurrence, it will be returned. """
|
||||||
if self._cache_complete:
|
if self._cache_complete:
|
||||||
gen = self._cache
|
gen = self._cache
|
||||||
else:
|
else:
|
||||||
@@ -201,6 +210,10 @@ class rrulebase(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def between(self, after, before, inc=False):
|
def between(self, after, before, inc=False):
|
||||||
|
""" Returns all the occurrences of the rrule between after and before.
|
||||||
|
The inc keyword defines what happens if after and/or before are
|
||||||
|
themselves occurrences. With inc=True, they will be included in the
|
||||||
|
list, if they are found in the recurrence set. """
|
||||||
if self._cache_complete:
|
if self._cache_complete:
|
||||||
gen = self._cache
|
gen = self._cache
|
||||||
else:
|
else:
|
||||||
@@ -229,7 +242,93 @@ class rrulebase(object):
|
|||||||
l.append(i)
|
l.append(i)
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
|
||||||
class rrule(rrulebase):
|
class rrule(rrulebase):
|
||||||
|
"""
|
||||||
|
That's the base of the rrule operation. It accepts all the keywords
|
||||||
|
defined in the RFC as its constructor parameters (except byday,
|
||||||
|
which was renamed to byweekday) and more. The constructor prototype is::
|
||||||
|
|
||||||
|
rrule(freq)
|
||||||
|
|
||||||
|
Where freq must be one of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY,
|
||||||
|
or SECONDLY.
|
||||||
|
|
||||||
|
Additionally, it supports the following keyword arguments:
|
||||||
|
|
||||||
|
:param cache:
|
||||||
|
If given, it must be a boolean value specifying to enable or disable
|
||||||
|
caching of results. If you will use the same rrule instance multiple
|
||||||
|
times, enabling caching will improve the performance considerably.
|
||||||
|
:param dtstart:
|
||||||
|
The recurrence start. Besides being the base for the recurrence,
|
||||||
|
missing parameters in the final recurrence instances will also be
|
||||||
|
extracted from this date. If not given, datetime.now() will be used
|
||||||
|
instead.
|
||||||
|
:param interval:
|
||||||
|
The interval between each freq iteration. For example, when using
|
||||||
|
YEARLY, an interval of 2 means once every two years, but with HOURLY,
|
||||||
|
it means once every two hours. The default interval is 1.
|
||||||
|
:param wkst:
|
||||||
|
The week start day. Must be one of the MO, TU, WE constants, or an
|
||||||
|
integer, specifying the first day of the week. This will affect
|
||||||
|
recurrences based on weekly periods. The default week start is got
|
||||||
|
from calendar.firstweekday(), and may be modified by
|
||||||
|
calendar.setfirstweekday().
|
||||||
|
:param count:
|
||||||
|
How many occurrences will be generated.
|
||||||
|
:param until:
|
||||||
|
If given, this must be a datetime instance, that will specify the
|
||||||
|
limit of the recurrence. If a recurrence instance happens to be the
|
||||||
|
same as the datetime instance given in the until keyword, this will
|
||||||
|
be the last occurrence.
|
||||||
|
:param bysetpos:
|
||||||
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
|
positive or negative. Each given integer will specify an occurrence
|
||||||
|
number, corresponding to the nth occurrence of the rule inside the
|
||||||
|
frequency period. For example, a bysetpos of -1 if combined with a
|
||||||
|
MONTHLY frequency, and a byweekday of (MO, TU, WE, TH, FR), will
|
||||||
|
result in the last work day of every month.
|
||||||
|
:param bymonth:
|
||||||
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
|
meaning the months to apply the recurrence to.
|
||||||
|
:param bymonthday:
|
||||||
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
|
meaning the month days to apply the recurrence to.
|
||||||
|
:param byyearday:
|
||||||
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
|
meaning the year days to apply the recurrence to.
|
||||||
|
:param byweekno:
|
||||||
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
|
meaning the week numbers to apply the recurrence to. Week numbers
|
||||||
|
have the meaning described in ISO8601, that is, the first week of
|
||||||
|
the year is that containing at least four days of the new year.
|
||||||
|
:param byweekday:
|
||||||
|
If given, it must be either an integer (0 == MO), a sequence of
|
||||||
|
integers, one of the weekday constants (MO, TU, etc), or a sequence
|
||||||
|
of these constants. When given, these variables will define the
|
||||||
|
weekdays where the recurrence will be applied. It's also possible to
|
||||||
|
use an argument n for the weekday instances, which will mean the nth
|
||||||
|
occurrence of this weekday in the period. For example, with MONTHLY,
|
||||||
|
or with YEARLY and BYMONTH, using FR(+1) in byweekday will specify the
|
||||||
|
first friday of the month where the recurrence happens. Notice that in
|
||||||
|
the RFC documentation, this is specified as BYDAY, but was renamed to
|
||||||
|
avoid the ambiguity of that keyword.
|
||||||
|
:param byhour:
|
||||||
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
|
meaning the hours to apply the recurrence to.
|
||||||
|
:param byminute:
|
||||||
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
|
meaning the minutes to apply the recurrence to.
|
||||||
|
:param bysecond:
|
||||||
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
|
meaning the seconds to apply the recurrence to.
|
||||||
|
:param byeaster:
|
||||||
|
If given, it must be either an integer, or a sequence of integers,
|
||||||
|
positive or negative. Each integer will define an offset from the
|
||||||
|
Easter Sunday. Passing the offset 0 to byeaster will yield the Easter
|
||||||
|
Sunday itself. This is an extension to the RFC specification.
|
||||||
|
"""
|
||||||
def __init__(self, freq, dtstart=None,
|
def __init__(self, freq, dtstart=None,
|
||||||
interval=1, wkst=None, count=None, until=None, bysetpos=None,
|
interval=1, wkst=None, count=None, until=None, bysetpos=None,
|
||||||
bymonth=None, bymonthday=None, byyearday=None, byeaster=None,
|
bymonth=None, bymonthday=None, byyearday=None, byeaster=None,
|
||||||
@@ -249,15 +348,18 @@ class rrule(rrulebase):
|
|||||||
self._freq = freq
|
self._freq = freq
|
||||||
self._interval = interval
|
self._interval = interval
|
||||||
self._count = count
|
self._count = count
|
||||||
|
|
||||||
if until and not isinstance(until, datetime.datetime):
|
if until and not isinstance(until, datetime.datetime):
|
||||||
until = datetime.datetime.fromordinal(until.toordinal())
|
until = datetime.datetime.fromordinal(until.toordinal())
|
||||||
self._until = until
|
self._until = until
|
||||||
|
|
||||||
if wkst is None:
|
if wkst is None:
|
||||||
self._wkst = calendar.firstweekday()
|
self._wkst = calendar.firstweekday()
|
||||||
elif isinstance(wkst, integer_types):
|
elif isinstance(wkst, integer_types):
|
||||||
self._wkst = wkst
|
self._wkst = wkst
|
||||||
else:
|
else:
|
||||||
self._wkst = wkst.weekday
|
self._wkst = wkst.weekday
|
||||||
|
|
||||||
if bysetpos is None:
|
if bysetpos is None:
|
||||||
self._bysetpos = None
|
self._bysetpos = None
|
||||||
elif isinstance(bysetpos, integer_types):
|
elif isinstance(bysetpos, integer_types):
|
||||||
@@ -271,30 +373,36 @@ class rrule(rrulebase):
|
|||||||
if pos == 0 or not (-366 <= pos <= 366):
|
if pos == 0 or not (-366 <= pos <= 366):
|
||||||
raise ValueError("bysetpos must be between 1 and 366, "
|
raise ValueError("bysetpos must be between 1 and 366, "
|
||||||
"or between -366 and -1")
|
"or between -366 and -1")
|
||||||
if not (byweekno or byyearday or bymonthday or
|
|
||||||
byweekday is not None or byeaster is not None):
|
if (byweekno is None and byyearday is None and bymonthday is None and
|
||||||
|
byweekday is None and byeaster is None):
|
||||||
if freq == YEARLY:
|
if freq == YEARLY:
|
||||||
if not bymonth:
|
if bymonth is None:
|
||||||
bymonth = dtstart.month
|
bymonth = dtstart.month
|
||||||
bymonthday = dtstart.day
|
bymonthday = dtstart.day
|
||||||
elif freq == MONTHLY:
|
elif freq == MONTHLY:
|
||||||
bymonthday = dtstart.day
|
bymonthday = dtstart.day
|
||||||
elif freq == WEEKLY:
|
elif freq == WEEKLY:
|
||||||
byweekday = dtstart.weekday()
|
byweekday = dtstart.weekday()
|
||||||
|
|
||||||
# bymonth
|
# bymonth
|
||||||
if not bymonth:
|
if bymonth is None:
|
||||||
self._bymonth = None
|
self._bymonth = None
|
||||||
elif isinstance(bymonth, integer_types):
|
|
||||||
self._bymonth = (bymonth,)
|
|
||||||
else:
|
else:
|
||||||
self._bymonth = tuple(bymonth)
|
if isinstance(bymonth, integer_types):
|
||||||
|
bymonth = (bymonth,)
|
||||||
|
|
||||||
|
self._bymonth = set(bymonth)
|
||||||
|
|
||||||
# byyearday
|
# byyearday
|
||||||
if not byyearday:
|
if byyearday is None:
|
||||||
self._byyearday = None
|
self._byyearday = None
|
||||||
elif isinstance(byyearday, integer_types):
|
|
||||||
self._byyearday = (byyearday,)
|
|
||||||
else:
|
else:
|
||||||
self._byyearday = tuple(byyearday)
|
if isinstance(byyearday, integer_types):
|
||||||
|
byyearday = (byyearday,)
|
||||||
|
|
||||||
|
self._byyearday = set(byyearday)
|
||||||
|
|
||||||
# byeaster
|
# byeaster
|
||||||
if byeaster is not None:
|
if byeaster is not None:
|
||||||
if not easter:
|
if not easter:
|
||||||
@@ -305,87 +413,104 @@ class rrule(rrulebase):
|
|||||||
self._byeaster = tuple(byeaster)
|
self._byeaster = tuple(byeaster)
|
||||||
else:
|
else:
|
||||||
self._byeaster = None
|
self._byeaster = None
|
||||||
|
|
||||||
# bymonthay
|
# bymonthay
|
||||||
if not bymonthday:
|
if bymonthday is None:
|
||||||
self._bymonthday = ()
|
self._bymonthday = ()
|
||||||
self._bynmonthday = ()
|
self._bynmonthday = ()
|
||||||
elif isinstance(bymonthday, integer_types):
|
|
||||||
if bymonthday < 0:
|
|
||||||
self._bynmonthday = (bymonthday,)
|
|
||||||
self._bymonthday = ()
|
|
||||||
else:
|
|
||||||
self._bymonthday = (bymonthday,)
|
|
||||||
self._bynmonthday = ()
|
|
||||||
else:
|
else:
|
||||||
self._bymonthday = tuple([x for x in bymonthday if x > 0])
|
if isinstance(bymonthday, integer_types):
|
||||||
self._bynmonthday = tuple([x for x in bymonthday if x < 0])
|
bymonthday = (bymonthday,)
|
||||||
|
|
||||||
|
self._bymonthday = set([x for x in bymonthday if x > 0])
|
||||||
|
self._bynmonthday = set([x for x in bymonthday if x < 0])
|
||||||
|
|
||||||
# byweekno
|
# byweekno
|
||||||
if byweekno is None:
|
if byweekno is None:
|
||||||
self._byweekno = None
|
self._byweekno = None
|
||||||
elif isinstance(byweekno, integer_types):
|
|
||||||
self._byweekno = (byweekno,)
|
|
||||||
else:
|
else:
|
||||||
self._byweekno = tuple(byweekno)
|
if isinstance(byweekno, integer_types):
|
||||||
|
byweekno = (byweekno,)
|
||||||
|
|
||||||
|
self._byweekno = set(byweekno)
|
||||||
|
|
||||||
# byweekday / bynweekday
|
# byweekday / bynweekday
|
||||||
if byweekday is None:
|
if byweekday is None:
|
||||||
self._byweekday = None
|
self._byweekday = None
|
||||||
self._bynweekday = None
|
self._bynweekday = None
|
||||||
elif isinstance(byweekday, integer_types):
|
|
||||||
self._byweekday = (byweekday,)
|
|
||||||
self._bynweekday = None
|
|
||||||
elif hasattr(byweekday, "n"):
|
|
||||||
if not byweekday.n or freq > MONTHLY:
|
|
||||||
self._byweekday = (byweekday.weekday,)
|
|
||||||
self._bynweekday = None
|
|
||||||
else:
|
|
||||||
self._bynweekday = ((byweekday.weekday, byweekday.n),)
|
|
||||||
self._byweekday = None
|
|
||||||
else:
|
else:
|
||||||
self._byweekday = []
|
if isinstance(byweekday, integer_types):
|
||||||
self._bynweekday = []
|
byweekday = (byweekday,)
|
||||||
|
elif hasattr(byweekday, "n"):
|
||||||
|
byweekday = (byweekday.weekday,)
|
||||||
|
|
||||||
|
self._byweekday = set()
|
||||||
|
self._bynweekday = set()
|
||||||
for wday in byweekday:
|
for wday in byweekday:
|
||||||
if isinstance(wday, integer_types):
|
if isinstance(wday, integer_types):
|
||||||
self._byweekday.append(wday)
|
self._byweekday.add(wday)
|
||||||
elif not wday.n or freq > MONTHLY:
|
elif not wday.n or freq > MONTHLY:
|
||||||
self._byweekday.append(wday.weekday)
|
self._byweekday.add(wday.weekday)
|
||||||
else:
|
else:
|
||||||
self._bynweekday.append((wday.weekday, wday.n))
|
self._bynweekday.add((wday.weekday, wday.n))
|
||||||
self._byweekday = tuple(self._byweekday)
|
|
||||||
self._bynweekday = tuple(self._bynweekday)
|
|
||||||
if not self._byweekday:
|
if not self._byweekday:
|
||||||
self._byweekday = None
|
self._byweekday = None
|
||||||
elif not self._bynweekday:
|
elif not self._bynweekday:
|
||||||
self._bynweekday = None
|
self._bynweekday = None
|
||||||
|
|
||||||
# byhour
|
# byhour
|
||||||
if byhour is None:
|
if byhour is None:
|
||||||
if freq < HOURLY:
|
if freq < HOURLY:
|
||||||
self._byhour = (dtstart.hour,)
|
self._byhour = set((dtstart.hour,))
|
||||||
else:
|
else:
|
||||||
self._byhour = None
|
self._byhour = None
|
||||||
elif isinstance(byhour, integer_types):
|
|
||||||
self._byhour = (byhour,)
|
|
||||||
else:
|
else:
|
||||||
self._byhour = tuple(byhour)
|
if isinstance(byhour, integer_types):
|
||||||
|
byhour = (byhour,)
|
||||||
|
|
||||||
|
if freq == HOURLY:
|
||||||
|
self._byhour = self.__construct_byset(start=dtstart.hour,
|
||||||
|
byxxx=byhour,
|
||||||
|
base=24)
|
||||||
|
else:
|
||||||
|
self._byhour = set(byhour)
|
||||||
|
|
||||||
# byminute
|
# byminute
|
||||||
if byminute is None:
|
if byminute is None:
|
||||||
if freq < MINUTELY:
|
if freq < MINUTELY:
|
||||||
self._byminute = (dtstart.minute,)
|
self._byminute = set((dtstart.minute,))
|
||||||
else:
|
else:
|
||||||
self._byminute = None
|
self._byminute = None
|
||||||
elif isinstance(byminute, integer_types):
|
|
||||||
self._byminute = (byminute,)
|
|
||||||
else:
|
else:
|
||||||
self._byminute = tuple(byminute)
|
if isinstance(byminute, integer_types):
|
||||||
|
byminute = (byminute,)
|
||||||
|
|
||||||
|
if freq == MINUTELY:
|
||||||
|
self._byminute = self.__construct_byset(start=dtstart.minute,
|
||||||
|
byxxx=byminute,
|
||||||
|
base=60)
|
||||||
|
else:
|
||||||
|
self._byminute = set(byminute)
|
||||||
|
|
||||||
# bysecond
|
# bysecond
|
||||||
if bysecond is None:
|
if bysecond is None:
|
||||||
if freq < SECONDLY:
|
if freq < SECONDLY:
|
||||||
self._bysecond = (dtstart.second,)
|
self._bysecond = ((dtstart.second,))
|
||||||
else:
|
else:
|
||||||
self._bysecond = None
|
self._bysecond = None
|
||||||
elif isinstance(bysecond, integer_types):
|
|
||||||
self._bysecond = (bysecond,)
|
|
||||||
else:
|
else:
|
||||||
self._bysecond = tuple(bysecond)
|
if isinstance(bysecond, integer_types):
|
||||||
|
bysecond = (bysecond,)
|
||||||
|
|
||||||
|
self._bysecond = set(bysecond)
|
||||||
|
|
||||||
|
if freq == SECONDLY:
|
||||||
|
self._bysecond = self.__construct_byset(start=dtstart.second,
|
||||||
|
byxxx=bysecond,
|
||||||
|
base=60)
|
||||||
|
else:
|
||||||
|
self._bysecond = set(bysecond)
|
||||||
|
|
||||||
if self._freq >= HOURLY:
|
if self._freq >= HOURLY:
|
||||||
self._timeset = None
|
self._timeset = None
|
||||||
@@ -395,8 +520,8 @@ class rrule(rrulebase):
|
|||||||
for minute in self._byminute:
|
for minute in self._byminute:
|
||||||
for second in self._bysecond:
|
for second in self._bysecond:
|
||||||
self._timeset.append(
|
self._timeset.append(
|
||||||
datetime.time(hour, minute, second,
|
datetime.time(hour, minute, second,
|
||||||
tzinfo=self._tzinfo))
|
tzinfo=self._tzinfo))
|
||||||
self._timeset.sort()
|
self._timeset.sort()
|
||||||
self._timeset = tuple(self._timeset)
|
self._timeset = tuple(self._timeset)
|
||||||
|
|
||||||
@@ -424,20 +549,20 @@ class rrule(rrulebase):
|
|||||||
ii = _iterinfo(self)
|
ii = _iterinfo(self)
|
||||||
ii.rebuild(year, month)
|
ii.rebuild(year, month)
|
||||||
|
|
||||||
getdayset = {YEARLY:ii.ydayset,
|
getdayset = {YEARLY: ii.ydayset,
|
||||||
MONTHLY:ii.mdayset,
|
MONTHLY: ii.mdayset,
|
||||||
WEEKLY:ii.wdayset,
|
WEEKLY: ii.wdayset,
|
||||||
DAILY:ii.ddayset,
|
DAILY: ii.ddayset,
|
||||||
HOURLY:ii.ddayset,
|
HOURLY: ii.ddayset,
|
||||||
MINUTELY:ii.ddayset,
|
MINUTELY: ii.ddayset,
|
||||||
SECONDLY:ii.ddayset}[freq]
|
SECONDLY: ii.ddayset}[freq]
|
||||||
|
|
||||||
if freq < HOURLY:
|
if freq < HOURLY:
|
||||||
timeset = self._timeset
|
timeset = self._timeset
|
||||||
else:
|
else:
|
||||||
gettimeset = {HOURLY:ii.htimeset,
|
gettimeset = {HOURLY: ii.htimeset,
|
||||||
MINUTELY:ii.mtimeset,
|
MINUTELY: ii.mtimeset,
|
||||||
SECONDLY:ii.stimeset}[freq]
|
SECONDLY: ii.stimeset}[freq]
|
||||||
if ((freq >= HOURLY and
|
if ((freq >= HOURLY and
|
||||||
self._byhour and hour not in self._byhour) or
|
self._byhour and hour not in self._byhour) or
|
||||||
(freq >= MINUTELY and
|
(freq >= MINUTELY and
|
||||||
@@ -466,11 +591,10 @@ class rrule(rrulebase):
|
|||||||
ii.mdaymask[i] not in bymonthday and
|
ii.mdaymask[i] not in bymonthday and
|
||||||
ii.nmdaymask[i] not in bynmonthday) or
|
ii.nmdaymask[i] not in bynmonthday) or
|
||||||
(byyearday and
|
(byyearday and
|
||||||
((i < ii.yearlen and i+1 not in byyearday
|
((i < ii.yearlen and i+1 not in byyearday and
|
||||||
and -ii.yearlen+i not in byyearday) or
|
-ii.yearlen+i not in byyearday) or
|
||||||
(i >= ii.yearlen and i+1-ii.yearlen not in byyearday
|
(i >= ii.yearlen and i+1-ii.yearlen not in byyearday and
|
||||||
and -ii.nextyearlen+i-ii.yearlen
|
-ii.nextyearlen+i-ii.yearlen not in byyearday)))):
|
||||||
not in byyearday)))):
|
|
||||||
dayset[i] = None
|
dayset[i] = None
|
||||||
filtered = True
|
filtered = True
|
||||||
|
|
||||||
@@ -484,7 +608,7 @@ class rrule(rrulebase):
|
|||||||
daypos, timepos = divmod(pos-1, len(timeset))
|
daypos, timepos = divmod(pos-1, len(timeset))
|
||||||
try:
|
try:
|
||||||
i = [x for x in dayset[start:end]
|
i = [x for x in dayset[start:end]
|
||||||
if x is not None][daypos]
|
if x is not None][daypos]
|
||||||
time = timeset[timepos]
|
time = timeset[timepos]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
@@ -559,60 +683,86 @@ class rrule(rrulebase):
|
|||||||
if filtered:
|
if filtered:
|
||||||
# Jump to one iteration before next day
|
# Jump to one iteration before next day
|
||||||
hour += ((23-hour)//interval)*interval
|
hour += ((23-hour)//interval)*interval
|
||||||
while True:
|
|
||||||
hour += interval
|
if byhour:
|
||||||
div, mod = divmod(hour, 24)
|
ndays, hour = self.__mod_distance(value=hour,
|
||||||
if div:
|
byxxx=self._byhour,
|
||||||
hour = mod
|
base=24)
|
||||||
day += div
|
else:
|
||||||
fixday = True
|
ndays, hour = divmod(hour+interval, 24)
|
||||||
if not byhour or hour in byhour:
|
|
||||||
break
|
if ndays:
|
||||||
|
day += ndays
|
||||||
|
fixday = True
|
||||||
|
|
||||||
timeset = gettimeset(hour, minute, second)
|
timeset = gettimeset(hour, minute, second)
|
||||||
elif freq == MINUTELY:
|
elif freq == MINUTELY:
|
||||||
if filtered:
|
if filtered:
|
||||||
# Jump to one iteration before next day
|
# Jump to one iteration before next day
|
||||||
minute += ((1439-(hour*60+minute))//interval)*interval
|
minute += ((1439-(hour*60+minute))//interval)*interval
|
||||||
while True:
|
|
||||||
minute += interval
|
valid = False
|
||||||
div, mod = divmod(minute, 60)
|
rep_rate = (24*60)
|
||||||
|
for j in range(rep_rate // gcd(interval, rep_rate)):
|
||||||
|
if byminute:
|
||||||
|
nhours, minute = \
|
||||||
|
self.__mod_distance(value=minute,
|
||||||
|
byxxx=self._byminute,
|
||||||
|
base=60)
|
||||||
|
else:
|
||||||
|
nhours, minute = divmod(minute+interval, 60)
|
||||||
|
|
||||||
|
div, hour = divmod(hour+nhours, 24)
|
||||||
if div:
|
if div:
|
||||||
minute = mod
|
day += div
|
||||||
hour += div
|
fixday = True
|
||||||
div, mod = divmod(hour, 24)
|
filtered = False
|
||||||
if div:
|
|
||||||
hour = mod
|
if not byhour or hour in byhour:
|
||||||
day += div
|
valid = True
|
||||||
fixday = True
|
|
||||||
filtered = False
|
|
||||||
if ((not byhour or hour in byhour) and
|
|
||||||
(not byminute or minute in byminute)):
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
raise ValueError('Invalid combination of interval and ' +
|
||||||
|
'byhour resulting in empty rule.')
|
||||||
|
|
||||||
timeset = gettimeset(hour, minute, second)
|
timeset = gettimeset(hour, minute, second)
|
||||||
elif freq == SECONDLY:
|
elif freq == SECONDLY:
|
||||||
if filtered:
|
if filtered:
|
||||||
# Jump to one iteration before next day
|
# Jump to one iteration before next day
|
||||||
second += (((86399-(hour*3600+minute*60+second))
|
second += (((86399-(hour*3600+minute*60+second))
|
||||||
//interval)*interval)
|
// interval)*interval)
|
||||||
while True:
|
|
||||||
second += self._interval
|
rep_rate = (24*3600)
|
||||||
div, mod = divmod(second, 60)
|
valid = False
|
||||||
|
for j in range(0, rep_rate // gcd(interval, rep_rate)):
|
||||||
|
if bysecond:
|
||||||
|
nminutes, second = \
|
||||||
|
self.__mod_distance(value=second,
|
||||||
|
byxxx=self._bysecond,
|
||||||
|
base=60)
|
||||||
|
else:
|
||||||
|
nminutes, second = divmod(second+interval, 60)
|
||||||
|
|
||||||
|
div, minute = divmod(minute+nminutes, 60)
|
||||||
if div:
|
if div:
|
||||||
second = mod
|
hour += div
|
||||||
minute += div
|
div, hour = divmod(hour, 24)
|
||||||
div, mod = divmod(minute, 60)
|
|
||||||
if div:
|
if div:
|
||||||
minute = mod
|
day += div
|
||||||
hour += div
|
fixday = True
|
||||||
div, mod = divmod(hour, 24)
|
|
||||||
if div:
|
|
||||||
hour = mod
|
|
||||||
day += div
|
|
||||||
fixday = True
|
|
||||||
if ((not byhour or hour in byhour) and
|
if ((not byhour or hour in byhour) and
|
||||||
(not byminute or minute in byminute) and
|
(not byminute or minute in byminute) and
|
||||||
(not bysecond or second in bysecond)):
|
(not bysecond or second in bysecond)):
|
||||||
|
valid = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
raise ValueError('Invalid combination of interval, ' +
|
||||||
|
'byhour and byminute resulting in empty' +
|
||||||
|
' rule.')
|
||||||
|
|
||||||
timeset = gettimeset(hour, minute, second)
|
timeset = gettimeset(hour, minute, second)
|
||||||
|
|
||||||
if fixday and day > 28:
|
if fixday and day > 28:
|
||||||
@@ -630,6 +780,80 @@ class rrule(rrulebase):
|
|||||||
daysinmonth = calendar.monthrange(year, month)[1]
|
daysinmonth = calendar.monthrange(year, month)[1]
|
||||||
ii.rebuild(year, month)
|
ii.rebuild(year, month)
|
||||||
|
|
||||||
|
def __construct_byset(self, start, byxxx, base):
|
||||||
|
"""
|
||||||
|
If a `BYXXX` sequence is passed to the constructor at the same level as
|
||||||
|
`FREQ` (e.g. `FREQ=HOURLY,BYHOUR={2,4,7},INTERVAL=3`), there are some
|
||||||
|
specifications which cannot be reached given some starting conditions.
|
||||||
|
|
||||||
|
This occurs whenever the interval is not coprime with the base of a
|
||||||
|
given unit and the difference between the starting position and the
|
||||||
|
ending position is not coprime with the greatest common denominator
|
||||||
|
between the interval and the base. For example, with a FREQ of hourly
|
||||||
|
starting at 17:00 and an interval of 4, the only valid values for
|
||||||
|
BYHOUR would be {21, 1, 5, 9, 13, 17}, because 4 and 24 are not
|
||||||
|
coprime.
|
||||||
|
|
||||||
|
:param:`start` specifies the starting position.
|
||||||
|
:param:`byxxx` is an iterable containing the list of allowed values.
|
||||||
|
:param:`base` is the largest allowable value for the specified
|
||||||
|
frequency (e.g. 24 hours, 60 minutes).
|
||||||
|
|
||||||
|
This does not preserve the type of the iterable, returning a set, since
|
||||||
|
the values should be unique and the order is irrelevant, this will
|
||||||
|
speed up later lookups.
|
||||||
|
|
||||||
|
In the event of an empty set, raises a :exception:`ValueError`, as this
|
||||||
|
results in an empty rrule.
|
||||||
|
"""
|
||||||
|
|
||||||
|
cset = set()
|
||||||
|
|
||||||
|
# Support a single byxxx value.
|
||||||
|
if isinstance(byxxx, integer_types):
|
||||||
|
byxxx = (byxxx)
|
||||||
|
|
||||||
|
for num in byxxx:
|
||||||
|
i_gcd = gcd(self._interval, base)
|
||||||
|
# Use divmod rather than % because we need to wrap negative nums.
|
||||||
|
if i_gcd == 1 or divmod(num - start, i_gcd)[1] == 0:
|
||||||
|
cset.add(num)
|
||||||
|
|
||||||
|
if len(cset) == 0:
|
||||||
|
raise ValueError("Invalid rrule byxxx generates an empty set.")
|
||||||
|
|
||||||
|
return cset
|
||||||
|
|
||||||
|
def __mod_distance(self, value, byxxx, base):
|
||||||
|
"""
|
||||||
|
Calculates the next value in a sequence where the `FREQ` parameter is
|
||||||
|
specified along with a `BYXXX` parameter at the same "level"
|
||||||
|
(e.g. `HOURLY` specified with `BYHOUR`).
|
||||||
|
|
||||||
|
:param:`value` is the old value of the component.
|
||||||
|
:param:`byxxx` is the `BYXXX` set, which should have been generated
|
||||||
|
by `rrule._construct_byset`, or something else which
|
||||||
|
checks that a valid rule is present.
|
||||||
|
:param:`base` is the largest allowable value for the specified
|
||||||
|
frequency (e.g. 24 hours, 60 minutes).
|
||||||
|
|
||||||
|
If a valid value is not found after `base` iterations (the maximum
|
||||||
|
number before the sequence would start to repeat), this raises a
|
||||||
|
:exception:`ValueError`, as no valid values were found.
|
||||||
|
|
||||||
|
This returns a tuple of `divmod(n*interval, base)`, where `n` is the
|
||||||
|
smallest number of `interval` repetitions until the next specified
|
||||||
|
value in `byxxx` is found.
|
||||||
|
"""
|
||||||
|
accumulator = 0
|
||||||
|
for ii in range(1, base + 1):
|
||||||
|
# Using divmod() over % to account for negative intervals
|
||||||
|
div, value = divmod(value + self._interval, base)
|
||||||
|
accumulator += div
|
||||||
|
if value in byxxx:
|
||||||
|
return (accumulator, value)
|
||||||
|
|
||||||
|
|
||||||
class _iterinfo(object):
|
class _iterinfo(object):
|
||||||
__slots__ = ["rrule", "lastyear", "lastmonth",
|
__slots__ = ["rrule", "lastyear", "lastmonth",
|
||||||
"yearlen", "nextyearlen", "yearordinal", "yearweekday",
|
"yearlen", "nextyearlen", "yearordinal", "yearweekday",
|
||||||
@@ -669,13 +893,13 @@ class _iterinfo(object):
|
|||||||
self.wnomask = None
|
self.wnomask = None
|
||||||
else:
|
else:
|
||||||
self.wnomask = [0]*(self.yearlen+7)
|
self.wnomask = [0]*(self.yearlen+7)
|
||||||
#no1wkst = firstwkst = self.wdaymask.index(rr._wkst)
|
# no1wkst = firstwkst = self.wdaymask.index(rr._wkst)
|
||||||
no1wkst = firstwkst = (7-self.yearweekday+rr._wkst)%7
|
no1wkst = firstwkst = (7-self.yearweekday+rr._wkst) % 7
|
||||||
if no1wkst >= 4:
|
if no1wkst >= 4:
|
||||||
no1wkst = 0
|
no1wkst = 0
|
||||||
# Number of days in the year, plus the days we got
|
# Number of days in the year, plus the days we got
|
||||||
# from last year.
|
# from last year.
|
||||||
wyearlen = self.yearlen+(self.yearweekday-rr._wkst)%7
|
wyearlen = self.yearlen+(self.yearweekday-rr._wkst) % 7
|
||||||
else:
|
else:
|
||||||
# Number of days in the year, minus the days we
|
# Number of days in the year, minus the days we
|
||||||
# left in last year.
|
# left in last year.
|
||||||
@@ -721,22 +945,22 @@ class _iterinfo(object):
|
|||||||
# this year.
|
# this year.
|
||||||
if -1 not in rr._byweekno:
|
if -1 not in rr._byweekno:
|
||||||
lyearweekday = datetime.date(year-1, 1, 1).weekday()
|
lyearweekday = datetime.date(year-1, 1, 1).weekday()
|
||||||
lno1wkst = (7-lyearweekday+rr._wkst)%7
|
lno1wkst = (7-lyearweekday+rr._wkst) % 7
|
||||||
lyearlen = 365+calendar.isleap(year-1)
|
lyearlen = 365+calendar.isleap(year-1)
|
||||||
if lno1wkst >= 4:
|
if lno1wkst >= 4:
|
||||||
lno1wkst = 0
|
lno1wkst = 0
|
||||||
lnumweeks = 52+(lyearlen+
|
lnumweeks = 52+(lyearlen +
|
||||||
(lyearweekday-rr._wkst)%7)%7//4
|
(lyearweekday-rr._wkst) % 7) % 7//4
|
||||||
else:
|
else:
|
||||||
lnumweeks = 52+(self.yearlen-no1wkst)%7//4
|
lnumweeks = 52+(self.yearlen-no1wkst) % 7//4
|
||||||
else:
|
else:
|
||||||
lnumweeks = -1
|
lnumweeks = -1
|
||||||
if lnumweeks in rr._byweekno:
|
if lnumweeks in rr._byweekno:
|
||||||
for i in range(no1wkst):
|
for i in range(no1wkst):
|
||||||
self.wnomask[i] = 1
|
self.wnomask[i] = 1
|
||||||
|
|
||||||
if (rr._bynweekday and
|
if (rr._bynweekday and (month != self.lastmonth or
|
||||||
(month != self.lastmonth or year != self.lastyear)):
|
year != self.lastyear)):
|
||||||
ranges = []
|
ranges = []
|
||||||
if rr._freq == YEARLY:
|
if rr._freq == YEARLY:
|
||||||
if rr._bymonth:
|
if rr._bymonth:
|
||||||
@@ -755,10 +979,10 @@ class _iterinfo(object):
|
|||||||
for wday, n in rr._bynweekday:
|
for wday, n in rr._bynweekday:
|
||||||
if n < 0:
|
if n < 0:
|
||||||
i = last+(n+1)*7
|
i = last+(n+1)*7
|
||||||
i -= (self.wdaymask[i]-wday)%7
|
i -= (self.wdaymask[i]-wday) % 7
|
||||||
else:
|
else:
|
||||||
i = first+(n-1)*7
|
i = first+(n-1)*7
|
||||||
i += (7-self.wdaymask[i]+wday)%7
|
i += (7-self.wdaymask[i]+wday) % 7
|
||||||
if first <= i <= last:
|
if first <= i <= last:
|
||||||
self.nwdaymask[i] = 1
|
self.nwdaymask[i] = 1
|
||||||
|
|
||||||
@@ -775,50 +999,50 @@ class _iterinfo(object):
|
|||||||
return list(range(self.yearlen)), 0, self.yearlen
|
return list(range(self.yearlen)), 0, self.yearlen
|
||||||
|
|
||||||
def mdayset(self, year, month, day):
|
def mdayset(self, year, month, day):
|
||||||
set = [None]*self.yearlen
|
dset = [None]*self.yearlen
|
||||||
start, end = self.mrange[month-1:month+1]
|
start, end = self.mrange[month-1:month+1]
|
||||||
for i in range(start, end):
|
for i in range(start, end):
|
||||||
set[i] = i
|
dset[i] = i
|
||||||
return set, start, end
|
return dset, start, end
|
||||||
|
|
||||||
def wdayset(self, year, month, day):
|
def wdayset(self, year, month, day):
|
||||||
# We need to handle cross-year weeks here.
|
# We need to handle cross-year weeks here.
|
||||||
set = [None]*(self.yearlen+7)
|
dset = [None]*(self.yearlen+7)
|
||||||
i = datetime.date(year, month, day).toordinal()-self.yearordinal
|
i = datetime.date(year, month, day).toordinal()-self.yearordinal
|
||||||
start = i
|
start = i
|
||||||
for j in range(7):
|
for j in range(7):
|
||||||
set[i] = i
|
dset[i] = i
|
||||||
i += 1
|
i += 1
|
||||||
#if (not (0 <= i < self.yearlen) or
|
# if (not (0 <= i < self.yearlen) or
|
||||||
# self.wdaymask[i] == self.rrule._wkst):
|
# self.wdaymask[i] == self.rrule._wkst):
|
||||||
# This will cross the year boundary, if necessary.
|
# This will cross the year boundary, if necessary.
|
||||||
if self.wdaymask[i] == self.rrule._wkst:
|
if self.wdaymask[i] == self.rrule._wkst:
|
||||||
break
|
break
|
||||||
return set, start, i
|
return dset, start, i
|
||||||
|
|
||||||
def ddayset(self, year, month, day):
|
def ddayset(self, year, month, day):
|
||||||
set = [None]*self.yearlen
|
dset = [None]*self.yearlen
|
||||||
i = datetime.date(year, month, day).toordinal()-self.yearordinal
|
i = datetime.date(year, month, day).toordinal()-self.yearordinal
|
||||||
set[i] = i
|
dset[i] = i
|
||||||
return set, i, i+1
|
return dset, i, i+1
|
||||||
|
|
||||||
def htimeset(self, hour, minute, second):
|
def htimeset(self, hour, minute, second):
|
||||||
set = []
|
tset = []
|
||||||
rr = self.rrule
|
rr = self.rrule
|
||||||
for minute in rr._byminute:
|
for minute in rr._byminute:
|
||||||
for second in rr._bysecond:
|
for second in rr._bysecond:
|
||||||
set.append(datetime.time(hour, minute, second,
|
tset.append(datetime.time(hour, minute, second,
|
||||||
tzinfo=rr._tzinfo))
|
tzinfo=rr._tzinfo))
|
||||||
set.sort()
|
tset.sort()
|
||||||
return set
|
return tset
|
||||||
|
|
||||||
def mtimeset(self, hour, minute, second):
|
def mtimeset(self, hour, minute, second):
|
||||||
set = []
|
tset = []
|
||||||
rr = self.rrule
|
rr = self.rrule
|
||||||
for second in rr._bysecond:
|
for second in rr._bysecond:
|
||||||
set.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo))
|
tset.append(datetime.time(hour, minute, second, tzinfo=rr._tzinfo))
|
||||||
set.sort()
|
tset.sort()
|
||||||
return set
|
return tset
|
||||||
|
|
||||||
def stimeset(self, hour, minute, second):
|
def stimeset(self, hour, minute, second):
|
||||||
return (datetime.time(hour, minute, second,
|
return (datetime.time(hour, minute, second,
|
||||||
@@ -826,6 +1050,12 @@ class _iterinfo(object):
|
|||||||
|
|
||||||
|
|
||||||
class rruleset(rrulebase):
|
class rruleset(rrulebase):
|
||||||
|
""" The rruleset type allows more complex recurrence setups, mixing
|
||||||
|
multiple rules, dates, exclusion rules, and exclusion dates. The type
|
||||||
|
constructor takes the following keyword arguments:
|
||||||
|
|
||||||
|
:param cache: If True, caching of results will be enabled, improving
|
||||||
|
performance of multiple queries considerably. """
|
||||||
|
|
||||||
class _genitem(object):
|
class _genitem(object):
|
||||||
def __init__(self, genlist, gen):
|
def __init__(self, genlist, gen):
|
||||||
@@ -865,15 +1095,26 @@ class rruleset(rrulebase):
|
|||||||
self._exdate = []
|
self._exdate = []
|
||||||
|
|
||||||
def rrule(self, rrule):
|
def rrule(self, rrule):
|
||||||
|
""" Include the given :py:class:`rrule` instance in the recurrence set
|
||||||
|
generation. """
|
||||||
self._rrule.append(rrule)
|
self._rrule.append(rrule)
|
||||||
|
|
||||||
def rdate(self, rdate):
|
def rdate(self, rdate):
|
||||||
|
""" Include the given :py:class:`datetime` instance in the recurrence
|
||||||
|
set generation. """
|
||||||
self._rdate.append(rdate)
|
self._rdate.append(rdate)
|
||||||
|
|
||||||
def exrule(self, exrule):
|
def exrule(self, exrule):
|
||||||
|
""" Include the given rrule instance in the recurrence set exclusion
|
||||||
|
list. Dates which are part of the given recurrence rules will not
|
||||||
|
be generated, even if some inclusive rrule or rdate matches them.
|
||||||
|
"""
|
||||||
self._exrule.append(exrule)
|
self._exrule.append(exrule)
|
||||||
|
|
||||||
def exdate(self, exdate):
|
def exdate(self, exdate):
|
||||||
|
""" Include the given datetime instance in the recurrence set
|
||||||
|
exclusion list. Dates included that way will not be generated,
|
||||||
|
even if some inclusive rrule or rdate matches them. """
|
||||||
self._exdate.append(exdate)
|
self._exdate.append(exdate)
|
||||||
|
|
||||||
def _iter(self):
|
def _iter(self):
|
||||||
@@ -905,6 +1146,7 @@ class rruleset(rrulebase):
|
|||||||
rlist.sort()
|
rlist.sort()
|
||||||
self._len = total
|
self._len = total
|
||||||
|
|
||||||
|
|
||||||
class _rrulestr(object):
|
class _rrulestr(object):
|
||||||
|
|
||||||
_freq_map = {"YEARLY": YEARLY,
|
_freq_map = {"YEARLY": YEARLY,
|
||||||
@@ -915,7 +1157,8 @@ class _rrulestr(object):
|
|||||||
"MINUTELY": MINUTELY,
|
"MINUTELY": MINUTELY,
|
||||||
"SECONDLY": SECONDLY}
|
"SECONDLY": SECONDLY}
|
||||||
|
|
||||||
_weekday_map = {"MO":0,"TU":1,"WE":2,"TH":3,"FR":4,"SA":5,"SU":6}
|
_weekday_map = {"MO": 0, "TU": 1, "WE": 2, "TH": 3,
|
||||||
|
"FR": 4, "SA": 5, "SU": 6}
|
||||||
|
|
||||||
def _handle_int(self, rrkwargs, name, value, **kwargs):
|
def _handle_int(self, rrkwargs, name, value, **kwargs):
|
||||||
rrkwargs[name.lower()] = int(value)
|
rrkwargs[name.lower()] = int(value)
|
||||||
@@ -923,17 +1166,17 @@ class _rrulestr(object):
|
|||||||
def _handle_int_list(self, rrkwargs, name, value, **kwargs):
|
def _handle_int_list(self, rrkwargs, name, value, **kwargs):
|
||||||
rrkwargs[name.lower()] = [int(x) for x in value.split(',')]
|
rrkwargs[name.lower()] = [int(x) for x in value.split(',')]
|
||||||
|
|
||||||
_handle_INTERVAL = _handle_int
|
_handle_INTERVAL = _handle_int
|
||||||
_handle_COUNT = _handle_int
|
_handle_COUNT = _handle_int
|
||||||
_handle_BYSETPOS = _handle_int_list
|
_handle_BYSETPOS = _handle_int_list
|
||||||
_handle_BYMONTH = _handle_int_list
|
_handle_BYMONTH = _handle_int_list
|
||||||
_handle_BYMONTHDAY = _handle_int_list
|
_handle_BYMONTHDAY = _handle_int_list
|
||||||
_handle_BYYEARDAY = _handle_int_list
|
_handle_BYYEARDAY = _handle_int_list
|
||||||
_handle_BYEASTER = _handle_int_list
|
_handle_BYEASTER = _handle_int_list
|
||||||
_handle_BYWEEKNO = _handle_int_list
|
_handle_BYWEEKNO = _handle_int_list
|
||||||
_handle_BYHOUR = _handle_int_list
|
_handle_BYHOUR = _handle_int_list
|
||||||
_handle_BYMINUTE = _handle_int_list
|
_handle_BYMINUTE = _handle_int_list
|
||||||
_handle_BYSECOND = _handle_int_list
|
_handle_BYSECOND = _handle_int_list
|
||||||
|
|
||||||
def _handle_FREQ(self, rrkwargs, name, value, **kwargs):
|
def _handle_FREQ(self, rrkwargs, name, value, **kwargs):
|
||||||
rrkwargs["freq"] = self._freq_map[value]
|
rrkwargs["freq"] = self._freq_map[value]
|
||||||
@@ -944,8 +1187,8 @@ class _rrulestr(object):
|
|||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
try:
|
try:
|
||||||
rrkwargs["until"] = parser.parse(value,
|
rrkwargs["until"] = parser.parse(value,
|
||||||
ignoretz=kwargs.get("ignoretz"),
|
ignoretz=kwargs.get("ignoretz"),
|
||||||
tzinfos=kwargs.get("tzinfos"))
|
tzinfos=kwargs.get("tzinfos"))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError("invalid until date")
|
raise ValueError("invalid until date")
|
||||||
|
|
||||||
@@ -960,7 +1203,8 @@ class _rrulestr(object):
|
|||||||
break
|
break
|
||||||
n = wday[:i] or None
|
n = wday[:i] or None
|
||||||
w = wday[i:]
|
w = wday[i:]
|
||||||
if n: n = int(n)
|
if n:
|
||||||
|
n = int(n)
|
||||||
l.append(weekdays[self._weekday_map[w]](n))
|
l.append(weekdays[self._weekday_map[w]](n))
|
||||||
rrkwargs["byweekday"] = l
|
rrkwargs["byweekday"] = l
|
||||||
|
|
||||||
@@ -1021,8 +1265,8 @@ class _rrulestr(object):
|
|||||||
i += 1
|
i += 1
|
||||||
else:
|
else:
|
||||||
lines = s.split()
|
lines = s.split()
|
||||||
if (not forceset and len(lines) == 1 and
|
if (not forceset and len(lines) == 1 and (s.find(':') == -1 or
|
||||||
(s.find(':') == -1 or s.startswith('RRULE:'))):
|
s.startswith('RRULE:'))):
|
||||||
return self._parse_rfc_rrule(lines[0], cache=cache,
|
return self._parse_rfc_rrule(lines[0], cache=cache,
|
||||||
dtstart=dtstart, ignoretz=ignoretz,
|
dtstart=dtstart, ignoretz=ignoretz,
|
||||||
tzinfos=tzinfos)
|
tzinfos=tzinfos)
|
||||||
@@ -1071,32 +1315,32 @@ class _rrulestr(object):
|
|||||||
tzinfos=tzinfos)
|
tzinfos=tzinfos)
|
||||||
else:
|
else:
|
||||||
raise ValueError("unsupported property: "+name)
|
raise ValueError("unsupported property: "+name)
|
||||||
if (forceset or len(rrulevals) > 1 or
|
if (forceset or len(rrulevals) > 1 or rdatevals
|
||||||
rdatevals or exrulevals or exdatevals):
|
or exrulevals or exdatevals):
|
||||||
if not parser and (rdatevals or exdatevals):
|
if not parser and (rdatevals or exdatevals):
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
set = rruleset(cache=cache)
|
rset = rruleset(cache=cache)
|
||||||
for value in rrulevals:
|
for value in rrulevals:
|
||||||
set.rrule(self._parse_rfc_rrule(value, dtstart=dtstart,
|
rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart,
|
||||||
ignoretz=ignoretz,
|
|
||||||
tzinfos=tzinfos))
|
|
||||||
for value in rdatevals:
|
|
||||||
for datestr in value.split(','):
|
|
||||||
set.rdate(parser.parse(datestr,
|
|
||||||
ignoretz=ignoretz,
|
|
||||||
tzinfos=tzinfos))
|
|
||||||
for value in exrulevals:
|
|
||||||
set.exrule(self._parse_rfc_rrule(value, dtstart=dtstart,
|
|
||||||
ignoretz=ignoretz,
|
ignoretz=ignoretz,
|
||||||
tzinfos=tzinfos))
|
tzinfos=tzinfos))
|
||||||
for value in exdatevals:
|
for value in rdatevals:
|
||||||
for datestr in value.split(','):
|
for datestr in value.split(','):
|
||||||
set.exdate(parser.parse(datestr,
|
rset.rdate(parser.parse(datestr,
|
||||||
ignoretz=ignoretz,
|
ignoretz=ignoretz,
|
||||||
tzinfos=tzinfos))
|
tzinfos=tzinfos))
|
||||||
|
for value in exrulevals:
|
||||||
|
rset.exrule(self._parse_rfc_rrule(value, dtstart=dtstart,
|
||||||
|
ignoretz=ignoretz,
|
||||||
|
tzinfos=tzinfos))
|
||||||
|
for value in exdatevals:
|
||||||
|
for datestr in value.split(','):
|
||||||
|
rset.exdate(parser.parse(datestr,
|
||||||
|
ignoretz=ignoretz,
|
||||||
|
tzinfos=tzinfos))
|
||||||
if compatible and dtstart:
|
if compatible and dtstart:
|
||||||
set.rdate(dtstart)
|
rset.rdate(dtstart)
|
||||||
return set
|
return rset
|
||||||
else:
|
else:
|
||||||
return self._parse_rfc_rrule(rrulevals[0],
|
return self._parse_rfc_rrule(rrulevals[0],
|
||||||
dtstart=dtstart,
|
dtstart=dtstart,
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Copyright (c) 2003-2007 Gustavo Niemeyer <gustavo@niemeyer.net>
|
This module offers timezone implementations subclassing the abstract
|
||||||
|
:py:`datetime.tzinfo` type. There are classes to handle tzfile format files
|
||||||
This module offers extensions to the standard Python
|
(usually are in :file:`/etc/localtime`, :file:`/usr/share/zoneinfo`, etc), TZ
|
||||||
datetime module.
|
environment string (in all known formats), given ranges (with help from
|
||||||
|
relative deltas), local machine timezone, fixed offset timezone, and UTC
|
||||||
|
timezone.
|
||||||
"""
|
"""
|
||||||
__license__ = "Simplified BSD"
|
|
||||||
|
|
||||||
from six import string_types, PY3
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from six import string_types, PY3
|
||||||
|
|
||||||
|
try:
|
||||||
|
from dateutil.tzwin import tzwin, tzwinlocal
|
||||||
|
except ImportError:
|
||||||
|
tzwin = tzwinlocal = None
|
||||||
|
|
||||||
relativedelta = None
|
relativedelta = None
|
||||||
parser = None
|
parser = None
|
||||||
rrule = None
|
rrule = None
|
||||||
@@ -21,10 +27,6 @@ rrule = None
|
|||||||
__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
|
__all__ = ["tzutc", "tzoffset", "tzlocal", "tzfile", "tzrange",
|
||||||
"tzstr", "tzical", "tzwin", "tzwinlocal", "gettz"]
|
"tzstr", "tzical", "tzwin", "tzwinlocal", "gettz"]
|
||||||
|
|
||||||
try:
|
|
||||||
from dateutil.tzwin import tzwin, tzwinlocal
|
|
||||||
except (ImportError, OSError):
|
|
||||||
tzwin, tzwinlocal = None, None
|
|
||||||
|
|
||||||
def tzname_in_python2(myfunc):
|
def tzname_in_python2(myfunc):
|
||||||
"""Change unicode output into bytestrings in Python 2
|
"""Change unicode output into bytestrings in Python 2
|
||||||
@@ -42,11 +44,12 @@ def tzname_in_python2(myfunc):
|
|||||||
ZERO = datetime.timedelta(0)
|
ZERO = datetime.timedelta(0)
|
||||||
EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal()
|
EPOCHORDINAL = datetime.datetime.utcfromtimestamp(0).toordinal()
|
||||||
|
|
||||||
|
|
||||||
class tzutc(datetime.tzinfo):
|
class tzutc(datetime.tzinfo):
|
||||||
|
|
||||||
def utcoffset(self, dt):
|
def utcoffset(self, dt):
|
||||||
return ZERO
|
return ZERO
|
||||||
|
|
||||||
def dst(self, dt):
|
def dst(self, dt):
|
||||||
return ZERO
|
return ZERO
|
||||||
|
|
||||||
@@ -66,6 +69,7 @@ class tzutc(datetime.tzinfo):
|
|||||||
|
|
||||||
__reduce__ = object.__reduce__
|
__reduce__ = object.__reduce__
|
||||||
|
|
||||||
|
|
||||||
class tzoffset(datetime.tzinfo):
|
class tzoffset(datetime.tzinfo):
|
||||||
|
|
||||||
def __init__(self, name, offset):
|
def __init__(self, name, offset):
|
||||||
@@ -96,6 +100,7 @@ class tzoffset(datetime.tzinfo):
|
|||||||
|
|
||||||
__reduce__ = object.__reduce__
|
__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)
|
||||||
@@ -123,25 +128,25 @@ class tzlocal(datetime.tzinfo):
|
|||||||
def _isdst(self, dt):
|
def _isdst(self, dt):
|
||||||
# We can't use mktime here. It is unstable when deciding if
|
# We can't use mktime here. It is unstable when deciding if
|
||||||
# the hour near to a change is DST or not.
|
# the hour near to a change is DST or not.
|
||||||
#
|
#
|
||||||
# timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour,
|
# timestamp = time.mktime((dt.year, dt.month, dt.day, dt.hour,
|
||||||
# dt.minute, dt.second, dt.weekday(), 0, -1))
|
# dt.minute, dt.second, dt.weekday(), 0, -1))
|
||||||
# return time.localtime(timestamp).tm_isdst
|
# return time.localtime(timestamp).tm_isdst
|
||||||
#
|
#
|
||||||
# The code above yields the following result:
|
# The code above yields the following result:
|
||||||
#
|
#
|
||||||
#>>> import tz, datetime
|
# >>> import tz, datetime
|
||||||
#>>> t = tz.tzlocal()
|
# >>> t = tz.tzlocal()
|
||||||
#>>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname()
|
# >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname()
|
||||||
#'BRDT'
|
# 'BRDT'
|
||||||
#>>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname()
|
# >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname()
|
||||||
#'BRST'
|
# 'BRST'
|
||||||
#>>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname()
|
# >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname()
|
||||||
#'BRST'
|
# 'BRST'
|
||||||
#>>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname()
|
# >>> datetime.datetime(2003,2,15,22,tzinfo=t).tzname()
|
||||||
#'BRDT'
|
# 'BRDT'
|
||||||
#>>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname()
|
# >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname()
|
||||||
#'BRDT'
|
# 'BRDT'
|
||||||
#
|
#
|
||||||
# Here is a more stable implementation:
|
# Here is a more stable implementation:
|
||||||
#
|
#
|
||||||
@@ -166,6 +171,7 @@ class tzlocal(datetime.tzinfo):
|
|||||||
|
|
||||||
__reduce__ = object.__reduce__
|
__reduce__ = object.__reduce__
|
||||||
|
|
||||||
|
|
||||||
class _ttinfo(object):
|
class _ttinfo(object):
|
||||||
__slots__ = ["offset", "delta", "isdst", "abbr", "isstd", "isgmt"]
|
__slots__ = ["offset", "delta", "isdst", "abbr", "isstd", "isgmt"]
|
||||||
|
|
||||||
@@ -205,15 +211,20 @@ class _ttinfo(object):
|
|||||||
if name in state:
|
if name in state:
|
||||||
setattr(self, name, state[name])
|
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
|
||||||
# ftp://ftp.iana.org/tz/tz*.tar.gz
|
# ftp://ftp.iana.org/tz/tz*.tar.gz
|
||||||
|
|
||||||
def __init__(self, fileobj):
|
def __init__(self, fileobj, filename=None):
|
||||||
|
file_opened_here = False
|
||||||
if isinstance(fileobj, string_types):
|
if isinstance(fileobj, string_types):
|
||||||
self._filename = fileobj
|
self._filename = fileobj
|
||||||
fileobj = open(fileobj, 'rb')
|
fileobj = open(fileobj, 'rb')
|
||||||
|
file_opened_here = True
|
||||||
|
elif filename is not None:
|
||||||
|
self._filename = filename
|
||||||
elif hasattr(fileobj, "name"):
|
elif hasattr(fileobj, "name"):
|
||||||
self._filename = fileobj.name
|
self._filename = fileobj.name
|
||||||
else:
|
else:
|
||||||
@@ -228,125 +239,128 @@ class tzfile(datetime.tzinfo):
|
|||||||
# six four-byte values of type long, written in a
|
# six four-byte values of type long, written in a
|
||||||
# ``standard'' byte order (the high-order byte
|
# ``standard'' byte order (the high-order byte
|
||||||
# of the value is written first).
|
# of the value is written first).
|
||||||
|
try:
|
||||||
|
if fileobj.read(4).decode() != "TZif":
|
||||||
|
raise ValueError("magic not found")
|
||||||
|
|
||||||
if fileobj.read(4).decode() != "TZif":
|
fileobj.read(16)
|
||||||
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.
|
||||||
# The number of UTC/local indicators stored in the file.
|
ttisstdcnt,
|
||||||
ttisgmtcnt,
|
|
||||||
|
|
||||||
# The number of standard/wall indicators stored in the file.
|
# The number of leap seconds for which data is
|
||||||
ttisstdcnt,
|
# stored in the file.
|
||||||
|
leapcnt,
|
||||||
# The number of leap seconds for which data is
|
|
||||||
# stored in the file.
|
|
||||||
leapcnt,
|
|
||||||
|
|
||||||
# The number of "transition times" for which data
|
# The number of "transition times" for which data
|
||||||
# is stored in the file.
|
# is stored in the file.
|
||||||
timecnt,
|
timecnt,
|
||||||
|
|
||||||
# The number of "local time types" for which data
|
# The number of "local time types" for which data
|
||||||
# is stored in the file (must not be zero).
|
# is stored in the file (must not be zero).
|
||||||
typecnt,
|
typecnt,
|
||||||
|
|
||||||
# The number of characters of "time zone
|
# The number of characters of "time zone
|
||||||
# abbreviation strings" stored in the file.
|
# abbreviation strings" stored in the file.
|
||||||
charcnt,
|
charcnt,
|
||||||
|
|
||||||
) = struct.unpack(">6l", fileobj.read(24))
|
) = struct.unpack(">6l", fileobj.read(24))
|
||||||
|
|
||||||
# The above header is followed by tzh_timecnt four-byte
|
# The above header is followed by tzh_timecnt four-byte
|
||||||
# values of type long, sorted in ascending order.
|
# values of type long, sorted in ascending order.
|
||||||
# These values are written in ``standard'' byte order.
|
# These values are written in ``standard'' byte order.
|
||||||
# Each is used as a transition time (as returned by
|
# Each is used as a transition time (as returned by
|
||||||
# time(2)) at which the rules for computing local time
|
# time(2)) at which the rules for computing local time
|
||||||
# change.
|
# change.
|
||||||
|
|
||||||
if timecnt:
|
if timecnt:
|
||||||
self._trans_list = struct.unpack(">%dl" % timecnt,
|
self._trans_list = struct.unpack(">%dl" % timecnt,
|
||||||
fileobj.read(timecnt*4))
|
fileobj.read(timecnt*4))
|
||||||
else:
|
else:
|
||||||
self._trans_list = []
|
self._trans_list = []
|
||||||
|
|
||||||
# Next come tzh_timecnt one-byte values of type unsigned
|
# Next come tzh_timecnt one-byte values of type unsigned
|
||||||
# char; each one tells which of the different types of
|
# char; each one tells which of the different types of
|
||||||
# ``local time'' types described in the file is associated
|
# ``local time'' types described in the file is associated
|
||||||
# with the same-indexed transition time. These values
|
# with the same-indexed transition time. These values
|
||||||
# serve as indices into an array of ttinfo structures that
|
# serve as indices into an array of ttinfo structures that
|
||||||
# appears next in the file.
|
# 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 = []
|
if timecnt:
|
||||||
|
self._trans_idx = struct.unpack(">%dB" % timecnt,
|
||||||
|
fileobj.read(timecnt))
|
||||||
|
else:
|
||||||
|
self._trans_idx = []
|
||||||
|
|
||||||
for i in range(typecnt):
|
# Each ttinfo structure is written as a four-byte value
|
||||||
ttinfo.append(struct.unpack(">lbb", fileobj.read(6)))
|
# 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.
|
||||||
|
|
||||||
abbr = fileobj.read(charcnt).decode()
|
ttinfo = []
|
||||||
|
|
||||||
# Then there are tzh_leapcnt pairs of four-byte
|
for i in range(typecnt):
|
||||||
# values, written in standard byte order; the
|
ttinfo.append(struct.unpack(">lbb", fileobj.read(6)))
|
||||||
# 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
|
abbr = fileobj.read(charcnt).decode()
|
||||||
if leapcnt:
|
|
||||||
leap = struct.unpack(">%dl" % (leapcnt*2),
|
|
||||||
fileobj.read(leapcnt*8))
|
|
||||||
|
|
||||||
# Then there are tzh_ttisstdcnt standard/wall
|
# Then there are tzh_leapcnt pairs of four-byte
|
||||||
# indicators, each stored as a one-byte value;
|
# values, written in standard byte order; the
|
||||||
# they tell whether the transition times associated
|
# first value of each pair gives the time (as
|
||||||
# with local time types were specified as standard
|
# returned by time(2)) at which a leap second
|
||||||
# time or wall clock time, and are used when
|
# occurs; the second gives the total number of
|
||||||
# a time zone file is used in handling POSIX-style
|
# leap seconds to be applied after the given time.
|
||||||
# time zone environment variables.
|
# The pairs of values are sorted in ascending order
|
||||||
|
# by time.
|
||||||
|
|
||||||
if ttisstdcnt:
|
# Not used, for now
|
||||||
isstd = struct.unpack(">%db" % ttisstdcnt,
|
# if leapcnt:
|
||||||
fileobj.read(ttisstdcnt))
|
# leap = struct.unpack(">%dl" % (leapcnt*2),
|
||||||
|
# fileobj.read(leapcnt*8))
|
||||||
|
|
||||||
# Finally, there are tzh_ttisgmtcnt UTC/local
|
# Then there are tzh_ttisstdcnt standard/wall
|
||||||
# indicators, each stored as a one-byte value;
|
# indicators, each stored as a one-byte value;
|
||||||
# they tell whether the transition times associated
|
# they tell whether the transition times associated
|
||||||
# with local time types were specified as UTC or
|
# with local time types were specified as standard
|
||||||
# local time, and are used when a time zone file
|
# time or wall clock time, and are used when
|
||||||
# is used in handling POSIX-style time zone envi-
|
# a time zone file is used in handling POSIX-style
|
||||||
# ronment variables.
|
# time zone environment variables.
|
||||||
|
|
||||||
if ttisgmtcnt:
|
if ttisstdcnt:
|
||||||
isgmt = struct.unpack(">%db" % ttisgmtcnt,
|
isstd = struct.unpack(">%db" % ttisstdcnt,
|
||||||
fileobj.read(ttisgmtcnt))
|
fileobj.read(ttisstdcnt))
|
||||||
|
|
||||||
# ** Everything has been read **
|
# 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 **
|
||||||
|
finally:
|
||||||
|
if file_opened_here:
|
||||||
|
fileobj.close()
|
||||||
|
|
||||||
# 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]
|
gmtoff, isdst, abbrind = ttinfo[i]
|
||||||
# Round to full-minutes if that's not the case. Python's
|
# Round to full-minutes if that's not the case. Python's
|
||||||
# datetime doesn't accept sub-minute timezones. Check
|
# datetime doesn't accept sub-minute timezones. Check
|
||||||
# http://python.org/sf/1447945 for some information.
|
# http://python.org/sf/1447945 for some information.
|
||||||
@@ -464,7 +478,7 @@ class tzfile(datetime.tzinfo):
|
|||||||
# However, this class stores historical changes in the
|
# However, this class stores historical changes in the
|
||||||
# dst offset, so I belive that this wouldn't be the right
|
# dst offset, so I belive that this wouldn't be the right
|
||||||
# way to implement this.
|
# way to implement this.
|
||||||
|
|
||||||
@tzname_in_python2
|
@tzname_in_python2
|
||||||
def tzname(self, dt):
|
def tzname(self, dt):
|
||||||
if not self._ttinfo_std:
|
if not self._ttinfo_std:
|
||||||
@@ -481,7 +495,6 @@ class tzfile(datetime.tzinfo):
|
|||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not self.__eq__(other)
|
return not self.__eq__(other)
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%s)" % (self.__class__.__name__, repr(self._filename))
|
return "%s(%s)" % (self.__class__.__name__, repr(self._filename))
|
||||||
|
|
||||||
@@ -490,8 +503,8 @@ class tzfile(datetime.tzinfo):
|
|||||||
raise ValueError("Unpickable %s class" % self.__class__.__name__)
|
raise ValueError("Unpickable %s class" % self.__class__.__name__)
|
||||||
return (self.__class__, (self._filename,))
|
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):
|
||||||
@@ -512,12 +525,12 @@ class tzrange(datetime.tzinfo):
|
|||||||
self._dst_offset = ZERO
|
self._dst_offset = ZERO
|
||||||
if dstabbr and 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 dstabbr and 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:
|
||||||
self._end_delta = end
|
self._end_delta = end
|
||||||
|
|
||||||
@@ -570,8 +583,9 @@ class tzrange(datetime.tzinfo):
|
|||||||
|
|
||||||
__reduce__ = object.__reduce__
|
__reduce__ = object.__reduce__
|
||||||
|
|
||||||
|
|
||||||
class tzstr(tzrange):
|
class tzstr(tzrange):
|
||||||
|
|
||||||
def __init__(self, s):
|
def __init__(self, s):
|
||||||
global parser
|
global parser
|
||||||
if not parser:
|
if not parser:
|
||||||
@@ -645,9 +659,10 @@ class tzstr(tzrange):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "%s(%s)" % (self.__class__.__name__, repr(self._s))
|
return "%s(%s)" % (self.__class__.__name__, repr(self._s))
|
||||||
|
|
||||||
|
|
||||||
class _tzicalvtzcomp(object):
|
class _tzicalvtzcomp(object):
|
||||||
def __init__(self, tzoffsetfrom, tzoffsetto, isdst,
|
def __init__(self, tzoffsetfrom, tzoffsetto, isdst,
|
||||||
tzname=None, rrule=None):
|
tzname=None, rrule=None):
|
||||||
self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom)
|
self.tzoffsetfrom = datetime.timedelta(seconds=tzoffsetfrom)
|
||||||
self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto)
|
self.tzoffsetto = datetime.timedelta(seconds=tzoffsetto)
|
||||||
self.tzoffsetdiff = self.tzoffsetto-self.tzoffsetfrom
|
self.tzoffsetdiff = self.tzoffsetto-self.tzoffsetfrom
|
||||||
@@ -655,6 +670,7 @@ class _tzicalvtzcomp(object):
|
|||||||
self.tzname = tzname
|
self.tzname = tzname
|
||||||
self.rrule = rrule
|
self.rrule = rrule
|
||||||
|
|
||||||
|
|
||||||
class _tzicalvtz(datetime.tzinfo):
|
class _tzicalvtz(datetime.tzinfo):
|
||||||
def __init__(self, tzid, comps=[]):
|
def __init__(self, tzid, comps=[]):
|
||||||
self._tzid = tzid
|
self._tzid = tzid
|
||||||
@@ -718,6 +734,7 @@ class _tzicalvtz(datetime.tzinfo):
|
|||||||
|
|
||||||
__reduce__ = object.__reduce__
|
__reduce__ = object.__reduce__
|
||||||
|
|
||||||
|
|
||||||
class tzical(object):
|
class tzical(object):
|
||||||
def __init__(self, fileobj):
|
def __init__(self, fileobj):
|
||||||
global rrule
|
global rrule
|
||||||
@@ -726,7 +743,8 @@ class tzical(object):
|
|||||||
|
|
||||||
if isinstance(fileobj, string_types):
|
if isinstance(fileobj, string_types):
|
||||||
self._s = fileobj
|
self._s = fileobj
|
||||||
fileobj = open(fileobj, 'r') # ical should be encoded in UTF-8 with CRLF
|
# ical should be encoded in UTF-8 with CRLF
|
||||||
|
fileobj = open(fileobj, 'r')
|
||||||
elif hasattr(fileobj, "name"):
|
elif hasattr(fileobj, "name"):
|
||||||
self._s = fileobj.name
|
self._s = fileobj.name
|
||||||
else:
|
else:
|
||||||
@@ -754,7 +772,7 @@ class tzical(object):
|
|||||||
if not s:
|
if not s:
|
||||||
raise ValueError("empty offset")
|
raise ValueError("empty offset")
|
||||||
if s[0] in ('+', '-'):
|
if s[0] in ('+', '-'):
|
||||||
signal = (-1, +1)[s[0]=='+']
|
signal = (-1, +1)[s[0] == '+']
|
||||||
s = s[1:]
|
s = s[1:]
|
||||||
else:
|
else:
|
||||||
signal = +1
|
signal = +1
|
||||||
@@ -815,7 +833,8 @@ class tzical(object):
|
|||||||
if not tzid:
|
if not tzid:
|
||||||
raise ValueError("mandatory TZID not found")
|
raise ValueError("mandatory TZID not found")
|
||||||
if not comps:
|
if not comps:
|
||||||
raise ValueError("at least one component is needed")
|
raise ValueError(
|
||||||
|
"at least one component is needed")
|
||||||
# Process vtimezone
|
# Process vtimezone
|
||||||
self._vtz[tzid] = _tzicalvtz(tzid, comps)
|
self._vtz[tzid] = _tzicalvtz(tzid, comps)
|
||||||
invtz = False
|
invtz = False
|
||||||
@@ -823,9 +842,11 @@ class tzical(object):
|
|||||||
if not founddtstart:
|
if not founddtstart:
|
||||||
raise ValueError("mandatory DTSTART not found")
|
raise ValueError("mandatory DTSTART not found")
|
||||||
if tzoffsetfrom is None:
|
if tzoffsetfrom is None:
|
||||||
raise ValueError("mandatory TZOFFSETFROM not found")
|
raise ValueError(
|
||||||
|
"mandatory TZOFFSETFROM not found")
|
||||||
if tzoffsetto is None:
|
if tzoffsetto is None:
|
||||||
raise ValueError("mandatory TZOFFSETFROM not found")
|
raise ValueError(
|
||||||
|
"mandatory TZOFFSETFROM not found")
|
||||||
# Process component
|
# Process component
|
||||||
rr = None
|
rr = None
|
||||||
if rrulelines:
|
if rrulelines:
|
||||||
@@ -848,15 +869,18 @@ class tzical(object):
|
|||||||
rrulelines.append(line)
|
rrulelines.append(line)
|
||||||
elif name == "TZOFFSETFROM":
|
elif name == "TZOFFSETFROM":
|
||||||
if parms:
|
if parms:
|
||||||
raise ValueError("unsupported %s parm: %s "%(name, parms[0]))
|
raise ValueError(
|
||||||
|
"unsupported %s parm: %s " % (name, parms[0]))
|
||||||
tzoffsetfrom = self._parse_offset(value)
|
tzoffsetfrom = self._parse_offset(value)
|
||||||
elif name == "TZOFFSETTO":
|
elif name == "TZOFFSETTO":
|
||||||
if parms:
|
if parms:
|
||||||
raise ValueError("unsupported TZOFFSETTO parm: "+parms[0])
|
raise ValueError(
|
||||||
|
"unsupported TZOFFSETTO parm: "+parms[0])
|
||||||
tzoffsetto = self._parse_offset(value)
|
tzoffsetto = self._parse_offset(value)
|
||||||
elif name == "TZNAME":
|
elif name == "TZNAME":
|
||||||
if parms:
|
if parms:
|
||||||
raise ValueError("unsupported TZNAME parm: "+parms[0])
|
raise ValueError(
|
||||||
|
"unsupported TZNAME parm: "+parms[0])
|
||||||
tzname = value
|
tzname = value
|
||||||
elif name == "COMMENT":
|
elif name == "COMMENT":
|
||||||
pass
|
pass
|
||||||
@@ -865,7 +889,8 @@ class tzical(object):
|
|||||||
else:
|
else:
|
||||||
if name == "TZID":
|
if name == "TZID":
|
||||||
if parms:
|
if parms:
|
||||||
raise ValueError("unsupported TZID parm: "+parms[0])
|
raise ValueError(
|
||||||
|
"unsupported TZID parm: "+parms[0])
|
||||||
tzid = value
|
tzid = value
|
||||||
elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"):
|
elif name in ("TZURL", "LAST-MODIFIED", "COMMENT"):
|
||||||
pass
|
pass
|
||||||
@@ -886,6 +911,7 @@ else:
|
|||||||
TZFILES = []
|
TZFILES = []
|
||||||
TZPATHS = []
|
TZPATHS = []
|
||||||
|
|
||||||
|
|
||||||
def gettz(name=None):
|
def gettz(name=None):
|
||||||
tz = None
|
tz = None
|
||||||
if not name:
|
if not name:
|
||||||
@@ -933,11 +959,11 @@ def gettz(name=None):
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
tz = None
|
tz = None
|
||||||
if tzwin:
|
if tzwin is not None:
|
||||||
try:
|
try:
|
||||||
tz = tzwin(name)
|
tz = tzwin(name)
|
||||||
except OSError:
|
except WindowsError:
|
||||||
pass
|
tz = None
|
||||||
if not tz:
|
if not tz:
|
||||||
from dateutil.zoneinfo import gettz
|
from dateutil.zoneinfo import gettz
|
||||||
tz = gettz(name)
|
tz = gettz(name)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# This code was originally contributed by Jeffrey Harris.
|
# This code was originally contributed by Jeffrey Harris.
|
||||||
import datetime
|
import datetime
|
||||||
import struct
|
import struct
|
||||||
import winreg
|
|
||||||
|
|
||||||
|
from six.moves import winreg
|
||||||
|
|
||||||
__all__ = ["tzwin", "tzwinlocal"]
|
__all__ = ["tzwin", "tzwinlocal"]
|
||||||
|
|
||||||
@@ -12,8 +12,8 @@ TZKEYNAMENT = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones"
|
|||||||
TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones"
|
TZKEYNAME9X = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Time Zones"
|
||||||
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
|
TZLOCALKEYNAME = r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation"
|
||||||
|
|
||||||
|
|
||||||
def _settzkeyname():
|
def _settzkeyname():
|
||||||
global TZKEYNAME
|
|
||||||
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
||||||
try:
|
try:
|
||||||
winreg.OpenKey(handle, TZKEYNAMENT).Close()
|
winreg.OpenKey(handle, TZKEYNAMENT).Close()
|
||||||
@@ -21,8 +21,10 @@ def _settzkeyname():
|
|||||||
except WindowsError:
|
except WindowsError:
|
||||||
TZKEYNAME = TZKEYNAME9X
|
TZKEYNAME = TZKEYNAME9X
|
||||||
handle.Close()
|
handle.Close()
|
||||||
|
return TZKEYNAME
|
||||||
|
|
||||||
|
TZKEYNAME = _settzkeyname()
|
||||||
|
|
||||||
_settzkeyname()
|
|
||||||
|
|
||||||
class tzwinbase(datetime.tzinfo):
|
class tzwinbase(datetime.tzinfo):
|
||||||
"""tzinfo class based on win32's timezones available in the registry."""
|
"""tzinfo class based on win32's timezones available in the registry."""
|
||||||
@@ -39,7 +41,7 @@ class tzwinbase(datetime.tzinfo):
|
|||||||
return datetime.timedelta(minutes=minutes)
|
return datetime.timedelta(minutes=minutes)
|
||||||
else:
|
else:
|
||||||
return datetime.timedelta(0)
|
return datetime.timedelta(0)
|
||||||
|
|
||||||
def tzname(self, dt):
|
def tzname(self, dt):
|
||||||
if self._isdst(dt):
|
if self._isdst(dt):
|
||||||
return self._dstname
|
return self._dstname
|
||||||
@@ -59,8 +61,11 @@ class tzwinbase(datetime.tzinfo):
|
|||||||
|
|
||||||
def display(self):
|
def display(self):
|
||||||
return self._display
|
return self._display
|
||||||
|
|
||||||
def _isdst(self, dt):
|
def _isdst(self, dt):
|
||||||
|
if not self._dstmonth:
|
||||||
|
# dstmonth == 0 signals the zone has no daylight saving time
|
||||||
|
return False
|
||||||
dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek,
|
dston = picknthweekday(dt.year, self._dstmonth, self._dstdayofweek,
|
||||||
self._dsthour, self._dstminute,
|
self._dsthour, self._dstminute,
|
||||||
self._dstweeknumber)
|
self._dstweeknumber)
|
||||||
@@ -78,31 +83,33 @@ class tzwin(tzwinbase):
|
|||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self._name = name
|
self._name = name
|
||||||
|
|
||||||
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
# multiple contexts only possible in 2.7 and 3.1, we still support 2.6
|
||||||
tzkey = winreg.OpenKey(handle, "%s\%s" % (TZKEYNAME, name))
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||||
keydict = valuestodict(tzkey)
|
with winreg.OpenKey(handle,
|
||||||
tzkey.Close()
|
"%s\%s" % (TZKEYNAME, name)) as tzkey:
|
||||||
handle.Close()
|
keydict = valuestodict(tzkey)
|
||||||
|
|
||||||
self._stdname = keydict["Std"].encode("iso-8859-1")
|
self._stdname = keydict["Std"].encode("iso-8859-1")
|
||||||
self._dstname = keydict["Dlt"].encode("iso-8859-1")
|
self._dstname = keydict["Dlt"].encode("iso-8859-1")
|
||||||
|
|
||||||
self._display = keydict["Display"]
|
self._display = keydict["Display"]
|
||||||
|
|
||||||
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
||||||
tup = struct.unpack("=3l16h", keydict["TZI"])
|
tup = struct.unpack("=3l16h", keydict["TZI"])
|
||||||
self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
|
self._stdoffset = -tup[0]-tup[1] # Bias + StandardBias * -1
|
||||||
self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1
|
self._dstoffset = self._stdoffset-tup[2] # + DaylightBias * -1
|
||||||
|
|
||||||
|
# for the meaning see the win32 TIME_ZONE_INFORMATION structure docs
|
||||||
|
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481(v=vs.85).aspx
|
||||||
(self._stdmonth,
|
(self._stdmonth,
|
||||||
self._stddayofweek, # Sunday = 0
|
self._stddayofweek, # Sunday = 0
|
||||||
self._stdweeknumber, # Last = 5
|
self._stdweeknumber, # Last = 5
|
||||||
self._stdhour,
|
self._stdhour,
|
||||||
self._stdminute) = tup[4:9]
|
self._stdminute) = tup[4:9]
|
||||||
|
|
||||||
(self._dstmonth,
|
(self._dstmonth,
|
||||||
self._dstdayofweek, # Sunday = 0
|
self._dstdayofweek, # Sunday = 0
|
||||||
self._dstweeknumber, # Last = 5
|
self._dstweeknumber, # Last = 5
|
||||||
self._dsthour,
|
self._dsthour,
|
||||||
self._dstminute) = tup[12:17]
|
self._dstminute) = tup[12:17]
|
||||||
|
|
||||||
@@ -114,61 +121,59 @@ class tzwin(tzwinbase):
|
|||||||
|
|
||||||
|
|
||||||
class tzwinlocal(tzwinbase):
|
class tzwinlocal(tzwinbase):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
handle = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
|
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as handle:
|
||||||
|
|
||||||
tzlocalkey = winreg.OpenKey(handle, TZLOCALKEYNAME)
|
with winreg.OpenKey(handle, TZLOCALKEYNAME) as tzlocalkey:
|
||||||
keydict = valuestodict(tzlocalkey)
|
keydict = valuestodict(tzlocalkey)
|
||||||
tzlocalkey.Close()
|
|
||||||
|
|
||||||
self._stdname = keydict["StandardName"].encode("iso-8859-1")
|
self._stdname = keydict["StandardName"].encode("iso-8859-1")
|
||||||
self._dstname = keydict["DaylightName"].encode("iso-8859-1")
|
self._dstname = keydict["DaylightName"].encode("iso-8859-1")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tzkey = winreg.OpenKey(handle, "%s\%s"%(TZKEYNAME, self._stdname))
|
with winreg.OpenKey(
|
||||||
_keydict = valuestodict(tzkey)
|
handle, "%s\%s" % (TZKEYNAME, self._stdname)) as tzkey:
|
||||||
self._display = _keydict["Display"]
|
_keydict = valuestodict(tzkey)
|
||||||
tzkey.Close()
|
self._display = _keydict["Display"]
|
||||||
except OSError:
|
except OSError:
|
||||||
self._display = None
|
self._display = None
|
||||||
|
|
||||||
handle.Close()
|
|
||||||
|
|
||||||
self._stdoffset = -keydict["Bias"]-keydict["StandardBias"]
|
self._stdoffset = -keydict["Bias"]-keydict["StandardBias"]
|
||||||
self._dstoffset = self._stdoffset-keydict["DaylightBias"]
|
self._dstoffset = self._stdoffset-keydict["DaylightBias"]
|
||||||
|
|
||||||
|
|
||||||
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
# See http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
|
||||||
tup = struct.unpack("=8h", keydict["StandardStart"])
|
tup = struct.unpack("=8h", keydict["StandardStart"])
|
||||||
|
|
||||||
(self._stdmonth,
|
(self._stdmonth,
|
||||||
self._stddayofweek, # Sunday = 0
|
self._stddayofweek, # Sunday = 0
|
||||||
self._stdweeknumber, # Last = 5
|
self._stdweeknumber, # Last = 5
|
||||||
self._stdhour,
|
self._stdhour,
|
||||||
self._stdminute) = tup[1:6]
|
self._stdminute) = tup[1:6]
|
||||||
|
|
||||||
tup = struct.unpack("=8h", keydict["DaylightStart"])
|
tup = struct.unpack("=8h", keydict["DaylightStart"])
|
||||||
|
|
||||||
(self._dstmonth,
|
(self._dstmonth,
|
||||||
self._dstdayofweek, # Sunday = 0
|
self._dstdayofweek, # Sunday = 0
|
||||||
self._dstweeknumber, # Last = 5
|
self._dstweeknumber, # Last = 5
|
||||||
self._dsthour,
|
self._dsthour,
|
||||||
self._dstminute) = tup[1:6]
|
self._dstminute) = tup[1:6]
|
||||||
|
|
||||||
def __reduce__(self):
|
def __reduce__(self):
|
||||||
return (self.__class__, ())
|
return (self.__class__, ())
|
||||||
|
|
||||||
|
|
||||||
def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
|
def picknthweekday(year, month, dayofweek, hour, minute, whichweek):
|
||||||
"""dayofweek == 0 means Sunday, whichweek 5 means last instance"""
|
"""dayofweek == 0 means Sunday, whichweek 5 means last instance"""
|
||||||
first = datetime.datetime(year, month, 1, hour, minute)
|
first = datetime.datetime(year, month, 1, hour, minute)
|
||||||
weekdayone = first.replace(day=((dayofweek-first.isoweekday())%7+1))
|
weekdayone = first.replace(day=((dayofweek-first.isoweekday()) % 7+1))
|
||||||
for n in range(whichweek):
|
for n in range(whichweek):
|
||||||
dt = weekdayone+(whichweek-n)*ONEWEEK
|
dt = weekdayone+(whichweek-n)*ONEWEEK
|
||||||
if dt.month == month:
|
if dt.month == month:
|
||||||
return dt
|
return dt
|
||||||
|
|
||||||
|
|
||||||
def valuestodict(key):
|
def valuestodict(key):
|
||||||
"""Convert a registry key's values to a dictionary."""
|
"""Convert a registry key's values to a dictionary."""
|
||||||
dict = {}
|
dict = {}
|
||||||
|
|||||||
@@ -1,109 +1,108 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
|
||||||
Copyright (c) 2003-2005 Gustavo Niemeyer <gustavo@niemeyer.net>
|
|
||||||
|
|
||||||
This module offers extensions to the standard Python
|
|
||||||
datetime module.
|
|
||||||
"""
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from subprocess import call
|
import warnings
|
||||||
|
import tempfile
|
||||||
|
import shutil
|
||||||
|
from subprocess import check_call
|
||||||
from tarfile import TarFile
|
from tarfile import TarFile
|
||||||
|
from pkgutil import get_data
|
||||||
|
from io import BytesIO
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
from dateutil.tz import tzfile
|
from dateutil.tz import tzfile
|
||||||
|
|
||||||
__author__ = "Tomi Pieviläinen <tomi.pievilainen@iki.fi>"
|
|
||||||
__license__ = "Simplified BSD"
|
|
||||||
|
|
||||||
__all__ = ["setcachesize", "gettz", "rebuild"]
|
__all__ = ["setcachesize", "gettz", "rebuild"]
|
||||||
|
|
||||||
CACHE = []
|
_ZONEFILENAME = "dateutil-zoneinfo.tar.gz"
|
||||||
CACHESIZE = 10
|
|
||||||
|
# python2.6 compatability. Note that TarFile.__exit__ != TarFile.close, but
|
||||||
|
# it's close enough for python2.6
|
||||||
|
_tar_open = TarFile.open
|
||||||
|
if not hasattr(TarFile, '__exit__'):
|
||||||
|
def _tar_open(*args, **kwargs):
|
||||||
|
return closing(TarFile.open(*args, **kwargs))
|
||||||
|
|
||||||
|
|
||||||
class tzfile(tzfile):
|
class tzfile(tzfile):
|
||||||
def __reduce__(self):
|
def __reduce__(self):
|
||||||
return (gettz, (self._filename,))
|
return (gettz, (self._filename,))
|
||||||
|
|
||||||
def getzoneinfofile():
|
|
||||||
filenames = sorted(os.listdir(os.path.join(os.path.dirname(__file__))))
|
|
||||||
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()
|
def getzoneinfofile_stream():
|
||||||
|
try:
|
||||||
|
return BytesIO(get_data(__name__, _ZONEFILENAME))
|
||||||
|
except IOError as e: # TODO switch to FileNotFoundError?
|
||||||
|
warnings.warn("I/O error({0}): {1}".format(e.errno, e.strerror))
|
||||||
|
return None
|
||||||
|
|
||||||
del getzoneinfofile
|
|
||||||
|
|
||||||
def setcachesize(size):
|
class ZoneInfoFile(object):
|
||||||
global CACHESIZE, CACHE
|
def __init__(self, zonefile_stream=None):
|
||||||
CACHESIZE = size
|
if zonefile_stream is not None:
|
||||||
del CACHE[size:]
|
with _tar_open(fileobj=zonefile_stream, mode='r') as tf:
|
||||||
|
# dict comprehension does not work on python2.6
|
||||||
|
# TODO: get back to the nicer syntax when we ditch python2.6
|
||||||
|
# self.zones = {zf.name: tzfile(tf.extractfile(zf),
|
||||||
|
# filename = zf.name)
|
||||||
|
# for zf in tf.getmembers() if zf.isfile()}
|
||||||
|
self.zones = dict((zf.name, tzfile(tf.extractfile(zf),
|
||||||
|
filename=zf.name))
|
||||||
|
for zf in tf.getmembers() if zf.isfile())
|
||||||
|
# deal with links: They'll point to their parent object. Less
|
||||||
|
# waste of memory
|
||||||
|
# links = {zl.name: self.zones[zl.linkname]
|
||||||
|
# for zl in tf.getmembers() if zl.islnk() or zl.issym()}
|
||||||
|
links = dict((zl.name, self.zones[zl.linkname])
|
||||||
|
for zl in tf.getmembers() if
|
||||||
|
zl.islnk() or zl.issym())
|
||||||
|
self.zones.update(links)
|
||||||
|
else:
|
||||||
|
self.zones = dict()
|
||||||
|
|
||||||
|
|
||||||
|
# The current API has gettz as a module function, although in fact it taps into
|
||||||
|
# a stateful class. So as a workaround for now, without changing the API, we
|
||||||
|
# will create a new "global" class instance the first time a user requests a
|
||||||
|
# timezone. Ugly, but adheres to the api.
|
||||||
|
#
|
||||||
|
# TODO: deprecate this.
|
||||||
|
_CLASS_ZONE_INSTANCE = list()
|
||||||
|
|
||||||
|
|
||||||
def gettz(name):
|
def gettz(name):
|
||||||
tzinfo = None
|
if len(_CLASS_ZONE_INSTANCE) == 0:
|
||||||
if ZONEINFOFILE:
|
_CLASS_ZONE_INSTANCE.append(ZoneInfoFile(getzoneinfofile_stream()))
|
||||||
for cachedname, tzinfo in CACHE:
|
return _CLASS_ZONE_INSTANCE[0].zones.get(name)
|
||||||
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"):
|
|
||||||
|
def rebuild(filename, tag=None, format="gz", zonegroups=[]):
|
||||||
"""Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar*
|
"""Rebuild the internal timezone info in dateutil/zoneinfo/zoneinfo*tar*
|
||||||
|
|
||||||
filename is the timezone tarball from ftp.iana.org/tz.
|
filename is the timezone tarball from ftp.iana.org/tz.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import tempfile, shutil
|
|
||||||
tmpdir = tempfile.mkdtemp()
|
tmpdir = tempfile.mkdtemp()
|
||||||
zonedir = os.path.join(tmpdir, "zoneinfo")
|
zonedir = os.path.join(tmpdir, "zoneinfo")
|
||||||
moduledir = os.path.dirname(__file__)
|
moduledir = os.path.dirname(__file__)
|
||||||
if tag: tag = "-"+tag
|
|
||||||
targetname = "zoneinfo%s.tar.%s" % (tag, format)
|
|
||||||
try:
|
try:
|
||||||
tf = TarFile.open(filename)
|
with _tar_open(filename) as tf:
|
||||||
# The "backwards" zone file contains links to other files, so must be
|
for name in zonegroups:
|
||||||
# processed as last
|
|
||||||
for name in sorted(tf.getnames(),
|
|
||||||
key=lambda k: k != "backward" and k or "z"):
|
|
||||||
if not (name.endswith(".sh") or
|
|
||||||
name.endswith(".tab") or
|
|
||||||
name == "leapseconds"):
|
|
||||||
tf.extract(name, tmpdir)
|
tf.extract(name, tmpdir)
|
||||||
filepath = os.path.join(tmpdir, name)
|
filepaths = [os.path.join(tmpdir, n) for n in zonegroups]
|
||||||
try:
|
try:
|
||||||
# zic will return errors for nontz files in the package
|
check_call(["zic", "-d", zonedir] + filepaths)
|
||||||
# such as the Makefile or README, so check_call cannot
|
except OSError as e:
|
||||||
# be used (or at least extra checks would be needed)
|
if e.errno == 2:
|
||||||
call(["zic", "-d", zonedir, filepath])
|
logging.error(
|
||||||
except OSError as e:
|
"Could not find zic. Perhaps you need to install "
|
||||||
if e.errno == 2:
|
"libc-bin or some other package that provides it, "
|
||||||
logging.error(
|
"or it's not in your PATH?")
|
||||||
"Could not find zic. Perhaps you need to install "
|
|
||||||
"libc-bin or some other package that provides it, "
|
|
||||||
"or it's not in your PATH?")
|
|
||||||
raise
|
raise
|
||||||
tf.close()
|
target = os.path.join(moduledir, _ZONEFILENAME)
|
||||||
target = os.path.join(moduledir, targetname)
|
with _tar_open(target, "w:%s" % format) as tf:
|
||||||
for entry in os.listdir(moduledir):
|
for entry in os.listdir(zonedir):
|
||||||
if entry.startswith("zoneinfo") and ".tar." in entry:
|
entrypath = os.path.join(zonedir, entry)
|
||||||
os.unlink(os.path.join(moduledir, entry))
|
tf.add(entrypath, 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:
|
finally:
|
||||||
shutil.rmtree(tmpdir)
|
shutil.rmtree(tmpdir)
|
||||||
|
|||||||
BIN
awx/lib/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz
Normal file
BIN
awx/lib/site-packages/dateutil/zoneinfo/dateutil-zoneinfo.tar.gz
Normal file
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user