Repository: pimutils/khal
Pull request #978 (2020-11-09 19:51:18)

Add a warning for the special case where RRULE:UNTIL is before DTSTART (#771,#790,#972)
#771, #790 and #972 all refer to events where the `RRULE:UNTIL` was before the event's `DTSTART`. This patch will stop khal from attempting to handle such events and will log a warning about it.

d7415: I'm not entirely sure that this is the best option. Part of me thinks we should still raise `UnsupportedRecurrence`, but add the message for context. Ideas welcome.
d7415: Thanks @ephase. Do you have any thoughts about raising the UnsupportedRecurrence? I'm leaning towards no as I've already said `This event will not be available in khal` so a Traceback may just be distracting. I'll probably wait a week or so before merging to allow for other opinions though.
ephase: I'm ok with you, Exception is not necessary here, your message is enough.

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 93aa290..0228ed9 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -14,6 +14,7 @@ not released
 
 * FIX `khal interactive` now accepts -a/-d options (as documented)
 * FIX Strip whitespace when loading `displayname` and `color` files
+* FIX Warn when loading events with a recurrence that finishes before it starts
 
 0.10.2
 ======
diff --git a/khal/icalendar.py b/khal/icalendar.py
index 1f39971..2a9e33f 100644
--- a/khal/icalendar.py
+++ b/khal/icalendar.py
@@ -257,9 +257,25 @@ def expand(vevent, href=''):
             # if python can deal with larger datetime values yet and b) pytz
             # doesn't know any larger transition times
             rrule._until = dt.datetime(2037, 12, 31)
-        elif events_tz and 'Z' in rrule_param.to_ical().decode():
-            rrule._until = pytz.UTC.localize(
-                rrule._until).astimezone(events_tz).replace(tzinfo=None)
+        else:
+            if events_tz and 'Z' in rrule_param.to_ical().decode():
+                rrule._until = pytz.UTC.localize(
+                    rrule._until).astimezone(events_tz).replace(tzinfo=None)
+
+            # rrule._until and dtstart could be dt.date or dt.datetime. They
+            # need to be the same for comparison
+            testuntil = rrule._until
+            if (type(dtstart) == dt.date and type(testuntil) == dt.datetime):
+                testuntil = testuntil.date()
+            teststart = dtstart
+            if (type(testuntil) == dt.date and type(teststart) == dt.datetime):
+                teststart = teststart.date()
+
+            if testuntil < teststart:
+                logger.warning(
+                    '{0}: Unsupported recurrence. UNTIL is before DTSTART.\n'
+                    'This event will not be available in khal.'.format(href))
+                return False
 
         rrule = map(sanitize_datetime, rrule)
 


Test patch:
diff --git a/tests/ics/event_dt_rrule_until_before_start.ics b/tests/ics/event_dt_rrule_until_before_start.ics
new file mode 100644
index 0000000..a074959
--- /dev/null
+++ b/tests/ics/event_dt_rrule_until_before_start.ics
@@ -0,0 +1,8 @@
+BEGIN:VEVENT
+SUMMARY:Stop recurring before we start
+DTSTART;VALUE=DATE-TIME:19801109T193000
+DTEND;VALUE=DATE-TIME:19801109T203000
+RRULE:FREQ=DAILY;UNTIL=19701119T203000Z
+DTSTAMP;VALUE=DATE-TIME:20140401T234817Z
+UID:V042MJ8B3SJNFXQOJL6P53OFMHJE8Z3VZWOV
+END:VEVENT
diff --git a/tests/khalendar_utils_test.py b/tests/khalendar_utils_test.py
index 6d46638..94b0c92 100644
--- a/tests/khalendar_utils_test.py
+++ b/tests/khalendar_utils_test.py
@@ -683,6 +683,13 @@ class TestSpecial(object):
         assert dtstart[-1] == (berlin.localize(dt.datetime(2014, 12, 3, 9, 30)),
                                berlin.localize(dt.datetime(2014, 12, 3, 10, 30)))
 
+    def test_event_dt_rrule_until_before_start(self):
+        """test handling if an RRULE's UNTIL is before the event's DTSTART"""
+        vevent = _get_vevent(_get_text('event_dt_rrule_until_before_start'))
+        dtstart = icalendar_helpers.expand(vevent, berlin)
+        # TODO test for logging message
+        assert dtstart is False
+
 
 simple_rdate = """BEGIN:VEVENT
 SUMMARY:Simple Rdate