aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Ganssle <paul@ganssle.io>2019-02-03 09:52:50 -0500
committerPaul Ganssle <paul@ganssle.io>2019-02-04 09:19:26 -0500
commit844f43ba2847999f32eb32818563fe225972d04d (patch)
tree848e6d4bba04c14a231f8482ee299d31bf9ad57c
parent95743955dd521f1ff47ca3737e9372c9a3e15959 (diff)
downloaddateutil-844f43ba2847999f32eb32818563fe225972d04d.tar.gz
Add EXDATE parameter parsing to rrulestr
Per RFC 5545 Section 3.8.5.1, exception dates should support VALUE and TZID parameters. This commit adds support for the full grammar of EXDATE.
-rw-r--r--dateutil/rrule.py51
-rw-r--r--dateutil/test/test_rrule.py3
2 files changed, 43 insertions, 11 deletions
diff --git a/dateutil/rrule.py b/dateutil/rrule.py
index ea450f2..3fe5e21 100644
--- a/dateutil/rrule.py
+++ b/dateutil/rrule.py
@@ -435,7 +435,7 @@ class rrule(rrulebase):
if not dtstart:
if until and until.tzinfo:
dtstart = datetime.datetime.now(tz=until.tzinfo).replace(microsecond=0)
- else:
+ else:
dtstart = datetime.datetime.now().replace(microsecond=0)
elif not isinstance(dtstart, datetime.datetime):
dtstart = datetime.datetime.fromordinal(dtstart.toordinal())
@@ -1629,10 +1629,48 @@ class _rrulestr(object):
raise ValueError("unsupported EXRULE parm: "+parm)
exrulevals.append(value)
elif name == "EXDATE":
+ value_found = False
+ TZID = None
for parm in parms:
- if parm != "VALUE=DATE-TIME":
- raise ValueError("unsupported EXDATE parm: "+parm)
- exdatevals.append(value)
+ if parm.startswith("TZID="):
+ try:
+ tzkey = TZID_NAMES[parm.split('TZID=')[-1]]
+ except KeyError:
+ continue
+ if tzids is None:
+ from . import tz
+ tzlookup = tz.gettz
+ elif callable(tzids):
+ tzlookup = tzids
+ else:
+ tzlookup = getattr(tzids, 'get', None)
+ if tzlookup is None:
+ msg = ('tzids must be a callable, ' +
+ 'mapping, or None, ' +
+ 'not %s' % tzids)
+ raise ValueError(msg)
+
+ TZID = tzlookup(tzkey)
+ continue
+ if parm not in {"VALUE=DATE-TIME", "VALUE=DATE"}:
+ raise ValueError("unsupported EXDATE parm: "
+ + parm)
+ else:
+ if value_found:
+ msg = ("Duplicate value parameter found in "
+ "EXDATE: " + parm)
+ raise ValueError(msg)
+ value_found = True
+ for datestr in value.split(','):
+ exdate = parser.parse(datestr, ignoretz=ignoretz,
+ tzinfos=tzinfos)
+ if TZID is not None:
+ if exdate.tzinfo is None:
+ exdate = exdate.replace(tzinfo=TZID)
+ else:
+ raise ValueError(
+ 'EXDATE specifies multiple timezone')
+ exdatevals.append(exdate)
elif name == "DTSTART":
# RFC 5445 3.8.2.4: The VALUE parameter is optional, but
# may be found only once.
@@ -1698,10 +1736,7 @@ class _rrulestr(object):
ignoretz=ignoretz,
tzinfos=tzinfos))
for value in exdatevals:
- for datestr in value.split(','):
- rset.exdate(parser.parse(datestr,
- ignoretz=ignoretz,
- tzinfos=tzinfos))
+ rset.exdate(value)
if compatible and dtstart:
rset.rdate(dtstart)
return rset
diff --git a/dateutil/test/test_rrule.py b/dateutil/test/test_rrule.py
index fbb943e..c43fea1 100644
--- a/dateutil/test/test_rrule.py
+++ b/dateutil/test/test_rrule.py
@@ -2853,7 +2853,6 @@ class RRuleTest(WarningTestMixin, unittest.TestCase):
datetime(1997, 9, 9, 9, 0),
datetime(1997, 9, 16, 9, 0)])
- @pytest.mark.xfail
def testStrSetExDateWithTZID(self):
BXL = tz.gettz('Europe/Brussels')
rr = rrulestr("DTSTART;TZID=Europe/Brussels:19970902T090000\n"
@@ -2888,7 +2887,6 @@ class RRuleTest(WarningTestMixin, unittest.TestCase):
rr = rrulestr(rrstr)
assert list(rr) == [datetime(1997, 9, 4, 9), datetime(1997, 9, 11, 9)]
- @pytest.mark.xfail
def testStrSetExDateValueDateTimeWithTZID(self):
BXL = tz.gettz('Europe/Brussels')
rrstr = '\n'.join([
@@ -2902,7 +2900,6 @@ class RRuleTest(WarningTestMixin, unittest.TestCase):
assert list(rr) == [datetime(1997, 9, 4, 9, tzinfo=BXL),
datetime(1997, 9, 11, 9, tzinfo=BXL)]
- @pytest.mark.xfail
def testStrSetExDateValueDate(self):
rrstr = '\n'.join([
"DTSTART;VALUE=DATE:19970902",