Convert EXDATE and RDATE to same type as DTSTART

Fixes #1146
Closes #1199
This commit is contained in:
Unrud 2021-12-20 00:55:29 +01:00
parent 537737da32
commit a20791e0c3
3 changed files with 65 additions and 2 deletions

View File

@ -24,11 +24,13 @@ Module for address books and calendar entries (see ``Item``).
""" """
import binascii import binascii
import contextlib
import math import math
import os import os
import sys import sys
from datetime import datetime, timedelta from datetime import datetime, timedelta
from hashlib import sha256 from hashlib import sha256
from itertools import chain
from typing import (Any, Callable, List, MutableMapping, Optional, Sequence, from typing import (Any, Callable, List, MutableMapping, Optional, Sequence,
Tuple) Tuple)
@ -142,6 +144,28 @@ def check_and_sanitize_items(
logger.debug("Quirks: Removing zero duration from %s in " logger.debug("Quirks: Removing zero duration from %s in "
"object %r", component_name, component_uid) "object %r", component_name, component_uid)
del component.duration 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 # vobject interprets recurrence rules on demand
try: try:
component.rruleset component.rruleset
@ -176,9 +200,9 @@ def check_and_sanitize_items(
else: else:
vobject_item.add("UID").value = object_uid vobject_item.add("UID").value = object_uid
else: else:
for i in vobject_items: for item in vobject_items:
raise ValueError("Item type %r not supported in %s collection" % 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] def check_and_sanitize_props(props: MutableMapping[Any, Any]

View File

@ -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

View File

@ -97,6 +97,12 @@ permissions: RrWw""")
assert xml.tag == xmlutils.make_clark("D:error") assert xml.tag == xmlutils.make_clark("D:error")
assert xml.find(xmlutils.make_clark("C:no-uid-conflict")) is not None 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: def test_add_todo(self) -> None:
"""Add a todo.""" """Add a todo."""
self.mkcalendar("/calendar.ics/") self.mkcalendar("/calendar.ics/")