From ae41547b73e4d1354ec1c43dd0021531aeff564c Mon Sep 17 00:00:00 2001 From: Allen Porter <allen@thebends.org> Date: Tue, 28 Mar 2023 03:25:44 -0700 Subject: [PATCH] Update calendar to always request start/end dates in local time rather than UTC (#90386) --- homeassistant/components/calendar/__init__.py | 2 +- homeassistant/components/google/calendar.py | 4 +-- .../components/local_calendar/calendar.py | 11 +++---- .../local_calendar/test_calendar.py | 33 ++++++++++++++++--- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/calendar/__init__.py b/homeassistant/components/calendar/__init__.py index 9af32446566..0b1c37cea5f 100644 --- a/homeassistant/components/calendar/__init__.py +++ b/homeassistant/components/calendar/__init__.py @@ -523,7 +523,7 @@ class CalendarEventView(http.HomeAssistantView): try: calendar_event_list = await entity.async_get_events( - request.app["hass"], start_date, end_date + request.app["hass"], dt.as_local(start_date), dt.as_local(end_date) ) except HomeAssistantError as err: _LOGGER.debug("Error reading events: %s", err) diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index 1e1072940ad..363b75c2c54 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -283,8 +283,8 @@ class CalendarSyncUpdateCoordinator(DataUpdateCoordinator[Timeline]): "Unable to get events: Sync from server has not completed" ) return self.data.overlapping( - dt_util.as_local(start_date), - dt_util.as_local(end_date), + start_date, + end_date, ) @property diff --git a/homeassistant/components/local_calendar/calendar.py b/homeassistant/components/local_calendar/calendar.py index 718c65ffce2..4b6d9444fd8 100644 --- a/homeassistant/components/local_calendar/calendar.py +++ b/homeassistant/components/local_calendar/calendar.py @@ -85,17 +85,16 @@ class LocalCalendarEntity(CalendarEntity): self, hass: HomeAssistant, start_date: datetime, end_date: datetime ) -> list[CalendarEvent]: """Get all events in a specific time frame.""" - events = self._calendar.timeline_tz(dt_util.DEFAULT_TIME_ZONE).overlapping( - dt_util.as_local(start_date), - dt_util.as_local(end_date), + events = self._calendar.timeline_tz(start_date.tzinfo).overlapping( + start_date, + end_date, ) return [_get_calendar_event(event) for event in events] async def async_update(self) -> None: """Update entity state with the next upcoming event.""" - events = self._calendar.timeline_tz(dt_util.DEFAULT_TIME_ZONE).active_after( - dt_util.now() - ) + now = dt_util.now() + events = self._calendar.timeline_tz(now.tzinfo).active_after(now) if event := next(events, None): self._event = _get_calendar_event(event) else: diff --git a/tests/components/local_calendar/test_calendar.py b/tests/components/local_calendar/test_calendar.py index 6bdb58cf65d..a2f13ea289d 100644 --- a/tests/components/local_calendar/test_calendar.py +++ b/tests/components/local_calendar/test_calendar.py @@ -37,10 +37,27 @@ async def test_empty_calendar( } +@pytest.mark.parametrize( + ("dtstart", "dtend"), + [ + ("1997-07-14T18:00:00+01:00", "1997-07-15T05:00:00+01:00"), + ("1997-07-14T17:00:00+00:00", "1997-07-15T04:00:00+00:00"), + ("1997-07-14T11:00:00-06:00", "1997-07-14T22:00:00-06:00"), + ("1997-07-14T10:00:00-07:00", "1997-07-14T21:00:00-07:00"), + ], +) async def test_api_date_time_event( - ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn + ws_client: ClientFixture, + setup_integration: None, + get_events: GetEventsFn, + dtstart: str, + dtend: str, ) -> None: - """Test an event with a start/end date time.""" + """Test an event with a start/end date time. + + Events created in various timezones are ultimately returned relative + to local home assistant timezone. + """ client = await ws_client() await client.cmd_result( "create", @@ -48,8 +65,8 @@ async def test_api_date_time_event( "entity_id": TEST_ENTITY, "event": { "summary": "Bastille Day Party", - "dtstart": "1997-07-14T17:00:00+00:00", - "dtend": "1997-07-15T04:00:00+00:00", + "dtstart": dtstart, + "dtend": dtend, }, }, ) @@ -63,6 +80,8 @@ async def test_api_date_time_event( } ] + # Query events in UTC + # Time range before event events = await get_events("1997-07-13T00:00:00Z", "1997-07-14T16:00:00Z") assert len(events) == 0 @@ -77,6 +96,12 @@ async def test_api_date_time_event( events = await get_events("1997-07-15T03:00:00Z", "1997-07-15T06:00:00Z") assert len(events) == 1 + # Query events overlapping with start and end but in another timezone + events = await get_events("1997-07-12T23:00:00-01:00", "1997-07-14T17:00:00-01:00") + assert len(events) == 1 + events = await get_events("1997-07-15T02:00:00-01:00", "1997-07-15T05:00:00-01:00") + assert len(events) == 1 + async def test_api_date_event( ws_client: ClientFixture, setup_integration: None, get_events: GetEventsFn -- GitLab