diff options
author | Paul Ganssle <paul@ganssle.io> | 2019-02-03 09:52:50 -0500 |
---|---|---|
committer | Paul Ganssle <paul@ganssle.io> | 2019-02-04 09:19:26 -0500 |
commit | 844f43ba2847999f32eb32818563fe225972d04d (patch) | |
tree | 848e6d4bba04c14a231f8482ee299d31bf9ad57c | |
parent | 95743955dd521f1ff47ca3737e9372c9a3e15959 (diff) | |
download | dateutil-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.py | 51 | ||||
-rw-r--r-- | dateutil/test/test_rrule.py | 3 |
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", |