From 65263bdef98e1d09eb5f33a833415f57175695c7 Mon Sep 17 00:00:00 2001 From: Lukas Date: Wed, 23 Oct 2019 08:08:38 +0200 Subject: [PATCH] Fix #28104 - CalDav support for floating datetimes (#28123) * Fix #28104 - CalDav support for floating datetimes Timzones are optional in CalDav It is possible that an entry contains neither a TZID, nor is an UTC time. When this is the case, it should be treated as a floating date-time value, which represent the same hour, minute, and second value regardless of which time zone is currently being observed. For Home-Assistant the correct timezone therefore is whatever is configured as local time in the settings. See https://www.kanzaki.com/docs/ical/dateTime.html * Revert "Fix #28104 - CalDav support for floating datetimes" This reverts commit cf32a6e39058e340816ae1e3ebd4a2c236b91964. * add test case: floating events fail with error without patch * Fix #28104 - CalDav support for floating datetimes Timzones are optional in CalDav It is possible that an entry contains neither a TZID, nor is an UTC time. When this is the case, it should be treated as a floating date-time value, which represent the same hour, minute, and second value regardless of which time zone is currently being observed. For Home-Assistant the correct timezone therefore is whatever is configured as local time in the settings. See https://www.kanzaki.com/docs/ical/dateTime.html * style fix --- homeassistant/components/caldav/calendar.py | 4 +++ tests/components/caldav/test_calendar.py | 36 +++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/homeassistant/components/caldav/calendar.py b/homeassistant/components/caldav/calendar.py index 2bbff2a6bc7..ad9dac1f727 100644 --- a/homeassistant/components/caldav/calendar.py +++ b/homeassistant/components/caldav/calendar.py @@ -278,6 +278,10 @@ 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 dt.as_local(dt.dt.datetime.combine(obj, dt.dt.time.min)) diff --git a/tests/components/caldav/test_calendar.py b/tests/components/caldav/test_calendar.py index 209ab780265..c0be635988a 100644 --- a/tests/components/caldav/test_calendar.py +++ b/tests/components/caldav/test_calendar.py @@ -111,6 +111,19 @@ LOCATION:San Francisco DESCRIPTION:Sunny day END:VEVENT END:VCALENDAR +""", + """BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//Global Corp.//CalDAV Client//EN +BEGIN:VEVENT +UID:8 +DTSTART:20171127T190000 +DTEND:20171127T200000 +SUMMARY:This is a floating Event +LOCATION:Hamburg +DESCRIPTION:What a day +END:VEVENT +END:VCALENDAR """, ] @@ -292,6 +305,29 @@ async def test_ongoing_event_different_tz(mock_now, hass, calendar): } +@patch("homeassistant.util.dt.now", return_value=_local_datetime(19, 10)) +async def test_ongoing_floating_event_returned(mock_now, hass, calendar): + """Test that floating events without timezones work.""" + assert await async_setup_component(hass, "calendar", {"calendar": CALDAV_CONFIG}) + await hass.async_block_till_done() + + state = hass.states.get("calendar.private") + print(dt.DEFAULT_TIME_ZONE) + print(state) + assert state.name == calendar.name + assert state.state == STATE_ON + assert dict(state.attributes) == { + "friendly_name": "Private", + "message": "This is a floating Event", + "all_day": False, + "offset_reached": False, + "start_time": "2017-11-27 19:00:00", + "end_time": "2017-11-27 20:00:00", + "location": "Hamburg", + "description": "What a day", + } + + @patch("homeassistant.util.dt.now", return_value=_local_datetime(8, 30)) async def test_ongoing_event_with_offset(mock_now, hass, calendar): """Test that the offset is taken into account."""