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 /dateutil | |
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.
Diffstat (limited to 'dateutil')
-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", |