Fix caldav calendars with custom timezones (#84955)

* Fix caldav calendars with custom timezones

* Revert whitespace change
This commit is contained in:
Allen Porter 2023-01-01 17:11:34 -08:00 committed by GitHub
parent 6220804639
commit a1588cd6af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 48 additions and 11 deletions

View File

@ -1,7 +1,7 @@
"""Support for WebDav Calendar."""
from __future__ import annotations
from datetime import datetime, timedelta
from datetime import date, datetime, timedelta
import logging
import re
@ -185,8 +185,8 @@ class WebDavCalendarData:
event_list.append(
CalendarEvent(
summary=self.get_attr_value(vevent, "summary") or "",
start=vevent.dtstart.value,
end=self.get_end_date(vevent),
start=self.to_local(vevent.dtstart.value),
end=self.to_local(self.get_end_date(vevent)),
location=self.get_attr_value(vevent, "location"),
description=self.get_attr_value(vevent, "description"),
)
@ -269,8 +269,8 @@ class WebDavCalendarData:
)
self.event = CalendarEvent(
summary=summary,
start=vevent.dtstart.value,
end=self.get_end_date(vevent),
start=self.to_local(vevent.dtstart.value),
end=self.to_local(self.get_end_date(vevent)),
location=self.get_attr_value(vevent, "location"),
description=self.get_attr_value(vevent, "description"),
)
@ -308,15 +308,23 @@ class WebDavCalendarData:
def to_datetime(obj):
"""Return a datetime."""
if isinstance(obj, datetime):
if obj.tzinfo is None:
# floating value, not bound to any time zone in particular
# represent same time regardless of which time zone is currently being observed
return obj.replace(tzinfo=dt.DEFAULT_TIME_ZONE)
return obj
return WebDavCalendarData.to_local(obj)
return dt.dt.datetime.combine(obj, dt.dt.time.min).replace(
tzinfo=dt.DEFAULT_TIME_ZONE
)
@staticmethod
def to_local(obj: datetime | date) -> datetime | date:
"""Return a datetime as a local datetime, leaving dates unchanged.
This handles giving floating times a timezone for comparison
with all day events and dropping the custom timezone object
used by the caldav client and dateutil so the datetime can be copied.
"""
if isinstance(obj, datetime):
return dt.as_local(obj)
return obj
@staticmethod
def get_attr_value(obj, attribute):
"""Return the value of the attribute if defined."""

View File

@ -226,6 +226,35 @@ DTEND:20151127T003000Z
RRULE:FREQ=HOURLY;INTERVAL=1;COUNT=12
END:VEVENT
END:VCALENDAR
""",
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Global Corp.//CalDAV Client//EN
BEGIN:VTIMEZONE
TZID:Europe/London
BEGIN:STANDARD
DTSTART:19961027T020000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
TZNAME:GMT
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19810329T010000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
TZNAME:BST
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
UID:15
DTSTAMP:20221125T000000Z
DTSTART;TZID=Europe/London:20221127T000000
DTEND;TZID=Europe/London:20221127T003000
SUMMARY:Event with a provided Timezone
END:VEVENT
END:VCALENDAR
""",
]
@ -929,7 +958,7 @@ async def test_get_events(hass, calendar, get_api_events):
await hass.async_block_till_done()
events = await get_api_events("calendar.private")
assert len(events) == 15
assert len(events) == 16
assert calendar.call