From 829d17e0e771aa7adb735032dc27b1a92c48bee9 Mon Sep 17 00:00:00 2001 From: Unrud Date: Wed, 25 Apr 2018 04:48:09 +0200 Subject: [PATCH 1/4] RRULE: Fix floating UNTIL with dateutil > 2.6.1 --- vobject/icalendar.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/vobject/icalendar.py b/vobject/icalendar.py index bcc4fab..6bfe382 100644 --- a/vobject/icalendar.py +++ b/vobject/icalendar.py @@ -445,12 +445,11 @@ def getrruleset(self, addRDate=False): # a Ruby iCalendar library escapes semi-colons in rrules, # so also remove any backslashes value = line.value.replace('\\', '') - rule = rrule.rrulestr( - value, dtstart=dtstart, - # If dtstart has no time zone, `until` - # shouldn't get one, either: - ignoretz=isinstance(dtstart, datetime.date)) - until = rule._until + # If dtstart has no time zone, `until` + # shouldn't get one, either: + ignoretz = (not isinstance(dtstart, datetime.datetime) or + dtstart.tzinfo is None) + until = rrule.rrulestr(value, ignoretz=ignoretz)._until if until is not None and isinstance(dtstart, datetime.datetime) and \ @@ -488,7 +487,12 @@ def getrruleset(self, addRDate=False): if dtstart.tzinfo is None: until = until.replace(tzinfo=None) - rule._until = until + value_without_until = ';'.join( + pair for pair in value.split(';') + if pair.split('=')[0].upper() != 'UNTIL') + rule = rrule.rrulestr(value_without_until, + dtstart=dtstart, ignoretz=ignoretz) + rule._until = until # add the rrule or exrule to the rruleset addfunc(rule) From 8e281298b0ddd3d48e4698b73cd29bcd591d28cb Mon Sep 17 00:00:00 2001 From: Unrud Date: Wed, 25 Apr 2018 13:25:31 +0200 Subject: [PATCH 2/4] RRULE: always ignore time zone dateutil raises an exception when UNTIL is UTC and ``dtstart`` is not set. This restores the old behaviour, because ``isinstance(dtstart, datetime.date)`` is always true. --- vobject/icalendar.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/vobject/icalendar.py b/vobject/icalendar.py index 6bfe382..05eef8a 100644 --- a/vobject/icalendar.py +++ b/vobject/icalendar.py @@ -445,11 +445,7 @@ def getrruleset(self, addRDate=False): # a Ruby iCalendar library escapes semi-colons in rrules, # so also remove any backslashes value = line.value.replace('\\', '') - # If dtstart has no time zone, `until` - # shouldn't get one, either: - ignoretz = (not isinstance(dtstart, datetime.datetime) or - dtstart.tzinfo is None) - until = rrule.rrulestr(value, ignoretz=ignoretz)._until + until = rrule.rrulestr(value, ignoretz=True)._until if until is not None and isinstance(dtstart, datetime.datetime) and \ @@ -491,7 +487,7 @@ def getrruleset(self, addRDate=False): pair for pair in value.split(';') if pair.split('=')[0].upper() != 'UNTIL') rule = rrule.rrulestr(value_without_until, - dtstart=dtstart, ignoretz=ignoretz) + dtstart=dtstart, ignoretz=True) rule._until = until # add the rrule or exrule to the rruleset From 2872f70b585a4c31147f32d6b5d3098426524234 Mon Sep 17 00:00:00 2001 From: Unrud Date: Wed, 25 Apr 2018 15:26:54 +0200 Subject: [PATCH 3/4] Revert "RRULE: always ignore time zone" This reverts commit 8e281298b0ddd3d48e4698b73cd29bcd591d28cb. The original behaviour was wrong. ``ignoretz=isinstance(dtstart, datetime.date)`` is always True, this causes vobject to always drop the time zone from UNTIL. If DTSTART has a time zone and UNTIL is UTC, the time zone of UNTIL gets replaced by the time zone of DTSTART without adjustment. --- vobject/icalendar.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/vobject/icalendar.py b/vobject/icalendar.py index 05eef8a..6bfe382 100644 --- a/vobject/icalendar.py +++ b/vobject/icalendar.py @@ -445,7 +445,11 @@ def getrruleset(self, addRDate=False): # a Ruby iCalendar library escapes semi-colons in rrules, # so also remove any backslashes value = line.value.replace('\\', '') - until = rrule.rrulestr(value, ignoretz=True)._until + # If dtstart has no time zone, `until` + # shouldn't get one, either: + ignoretz = (not isinstance(dtstart, datetime.datetime) or + dtstart.tzinfo is None) + until = rrule.rrulestr(value, ignoretz=ignoretz)._until if until is not None and isinstance(dtstart, datetime.datetime) and \ @@ -487,7 +491,7 @@ def getrruleset(self, addRDate=False): pair for pair in value.split(';') if pair.split('=')[0].upper() != 'UNTIL') rule = rrule.rrulestr(value_without_until, - dtstart=dtstart, ignoretz=True) + dtstart=dtstart, ignoretz=ignoretz) rule._until = until # add the rrule or exrule to the rruleset From 0ec6ad3f9ea51179abd7816fa137394feac2ea58 Mon Sep 17 00:00:00 2001 From: Unrud Date: Wed, 25 Apr 2018 15:35:22 +0200 Subject: [PATCH 4/4] RRULE: Add workaround for dateutil<=2.7.2 https://github.com/dateutil/dateutil/commit/284622691f286a19fd51ec4f2aeeb08618dea266#diff-0cf96106a7dd4a241fca4079eac4c0a4 --- vobject/icalendar.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/vobject/icalendar.py b/vobject/icalendar.py index 6bfe382..77e0c17 100644 --- a/vobject/icalendar.py +++ b/vobject/icalendar.py @@ -449,7 +449,15 @@ def getrruleset(self, addRDate=False): # shouldn't get one, either: ignoretz = (not isinstance(dtstart, datetime.datetime) or dtstart.tzinfo is None) - until = rrule.rrulestr(value, ignoretz=ignoretz)._until + try: + until = rrule.rrulestr(value, ignoretz=ignoretz)._until + except ValueError: + # WORKAROUND: dateutil<=2.7.2 doesn't set the time zone + # of dtstart + if ignoretz: + raise + utc_now = datetime.datetime.now(datetime.timezone.utc) + until = rrule.rrulestr(value, dtstart=utc_now)._until if until is not None and isinstance(dtstart, datetime.datetime) and \