Просмотр исходного кода

Convert EXDATE and RDATE to same type as DTSTART

Fixes #1146
Closes #1199
Unrud 4 лет назад
Родитель
Сommit
a20791e0c3

+ 26 - 2
radicale/item/__init__.py

@@ -24,11 +24,13 @@ Module for address books and calendar entries (see ``Item``).
 """
 
 import binascii
+import contextlib
 import math
 import os
 import sys
 from datetime import datetime, timedelta
 from hashlib import sha256
+from itertools import chain
 from typing import (Any, Callable, List, MutableMapping, Optional, Sequence,
                     Tuple)
 
@@ -142,6 +144,28 @@ def check_and_sanitize_items(
                 logger.debug("Quirks: Removing zero duration from %s in "
                              "object %r", component_name, component_uid)
                 del component.duration
+            # Workaround for Evolution
+            # EXDATE has value DATE even if DTSTART/DTEND is DATE-TIME.
+            # The RFC is vaguely formulated on the issue.
+            # To resolve the issue convert EXDATE and RDATE to
+            # the same type as DTDSTART
+            if hasattr(component, "dtstart"):
+                ref_date = component.dtstart.value
+                ref_value_param = component.dtstart.params.get("VALUE")
+                for dates in chain(component.contents.get("exdate", []),
+                                   component.contents.get("rdate", [])):
+                    replace_value_param = False
+                    for i, date in enumerate(dates.value):
+                        if type(date) != type(ref_date):
+                            replace_value_param = True
+                            dates.value[i] = ref_date.replace(
+                                date.year, date.month, date.day)
+                    if replace_value_param:
+                        if ref_value_param is None:
+                            with contextlib.suppress(KeyError):
+                                del dates.params["VALUE"]
+                        else:
+                            dates.params["VALUE"] = ref_value_param
             # vobject interprets recurrence rules on demand
             try:
                 component.rruleset
@@ -176,9 +200,9 @@ def check_and_sanitize_items(
                 else:
                     vobject_item.add("UID").value = object_uid
     else:
-        for i in vobject_items:
+        for item in vobject_items:
             raise ValueError("Item type %r not supported in %s collection" %
-                             (i.name, repr(tag) if tag else "generic"))
+                             (item.name, repr(tag) if tag else "generic"))
 
 
 def check_and_sanitize_props(props: MutableMapping[Any, Any]

+ 33 - 0
radicale/tests/static/event_mixed_datetime_and_date.ics

@@ -0,0 +1,33 @@
+BEGIN:VCALENDAR
+PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+TZID:Europe/Paris
+X-LIC-LOCATION:Europe/Paris
+BEGIN:DAYLIGHT
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+TZNAME:CEST
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+TZNAME:CET
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+CREATED:20130902T150157Z
+LAST-MODIFIED:20130902T150158Z
+DTSTAMP:20130902T150158Z
+UID:event_mixed_datetime_and_date
+SUMMARY:Event
+DTSTART;TZID=Europe/Paris:20130901T180000
+DTEND;TZID=Europe/Paris:20130901T190000
+RRULE:FREQ=DAILY;COUNT=3
+EXDATE;VALUE=DATE:20130902
+END:VEVENT
+END:VCALENDAR

+ 6 - 0
radicale/tests/test_base.py

@@ -97,6 +97,12 @@ permissions: RrWw""")
         assert xml.tag == xmlutils.make_clark("D:error")
         assert xml.find(xmlutils.make_clark("C:no-uid-conflict")) is not None
 
+    def test_add_event_with_mixed_datetime_and_date(self) -> None:
+        """Test event with DTSTART as DATE-TIME and EXDATE as DATE."""
+        self.mkcalendar("/calendar.ics/")
+        event = get_file_content("event_mixed_datetime_and_date.ics")
+        self.put("/calendar.ics/event.ics", event)
+
     def test_add_todo(self) -> None:
         """Add a todo."""
         self.mkcalendar("/calendar.ics/")