Fix caldav TZ interpretation of all day events (#48642)

This commit is contained in:
TOM 2021-06-29 02:07:29 -05:00 committed by GitHub
parent bb4d3bfc60
commit 3f66709882
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 156 additions and 40 deletions

View File

@ -310,7 +310,9 @@ class WebDavCalendarData:
# represent same time regardless of which time zone is currently being observed # represent same time regardless of which time zone is currently being observed
return obj.replace(tzinfo=dt.DEFAULT_TIME_ZONE) return obj.replace(tzinfo=dt.DEFAULT_TIME_ZONE)
return obj return obj
return dt.as_local(dt.dt.datetime.combine(obj, dt.dt.time.min)) return dt.dt.datetime.combine(obj, dt.dt.time.min).replace(
tzinfo=dt.DEFAULT_TIME_ZONE
)
@staticmethod @staticmethod
def get_attr_value(obj, attribute): def get_attr_value(obj, attribute):

View File

@ -222,6 +222,39 @@ CALDAV_CONFIG = {
"custom_calendars": [], "custom_calendars": [],
} }
ORIG_TZ = dt.DEFAULT_TIME_ZONE
@pytest.fixture(autouse=True)
def reset_tz():
"""Restore the default TZ after test runs."""
yield
dt.DEFAULT_TIME_ZONE = ORIG_TZ
@pytest.fixture
def set_tz(request):
"""Set the default TZ to the one requested."""
return request.getfixturevalue(request.param)
@pytest.fixture
def utc():
"""Set the default TZ to UTC."""
dt.set_default_time_zone(dt.get_time_zone("UTC"))
@pytest.fixture
def new_york():
"""Set the default TZ to America/New_York."""
dt.set_default_time_zone(dt.get_time_zone("America/New_York"))
@pytest.fixture
def baghdad():
"""Set the default TZ to Asia/Baghdad."""
dt.set_default_time_zone(dt.get_time_zone("Asia/Baghdad"))
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def mock_http(hass): def mock_http(hass):
@ -524,30 +557,72 @@ async def test_no_result_with_filtering(mock_now, hass, calendar):
assert state.state == "off" assert state.state == "off"
@patch("homeassistant.util.dt.now", return_value=_local_datetime(17, 30)) async def _day_event_returned(hass, calendar, config, date_time):
async def test_all_day_event_returned(mock_now, hass, calendar): with patch("homeassistant.util.dt.now", return_value=date_time):
"""Test that the event lasting the whole day is returned.""" assert await async_setup_component(hass, "calendar", {"calendar": config})
await hass.async_block_till_done()
state = hass.states.get("calendar.private_private")
assert state.name == calendar.name
assert state.state == STATE_ON
assert dict(state.attributes) == {
"friendly_name": "Private",
"message": "This is an all day event",
"all_day": True,
"offset_reached": False,
"start_time": "2017-11-27 00:00:00",
"end_time": "2017-11-28 00:00:00",
"location": "Hamburg",
"description": "What a beautiful day",
}
@pytest.mark.parametrize("set_tz", ["utc", "new_york", "baghdad"], indirect=True)
async def test_all_day_event_returned_early(hass, calendar, set_tz):
"""Test that the event lasting the whole day is returned, if it's early in the local day."""
config = dict(CALDAV_CONFIG) config = dict(CALDAV_CONFIG)
config["custom_calendars"] = [ config["custom_calendars"] = [
{"name": "Private", "calendar": "Private", "search": ".*"} {"name": "Private", "calendar": "Private", "search": ".*"}
] ]
assert await async_setup_component(hass, "calendar", {"calendar": config}) await _day_event_returned(
await hass.async_block_till_done() hass,
calendar,
config,
datetime.datetime(2017, 11, 27, 0, 30).replace(tzinfo=dt.DEFAULT_TIME_ZONE),
)
state = hass.states.get("calendar.private_private")
assert state.name == calendar.name @pytest.mark.parametrize("set_tz", ["utc", "new_york", "baghdad"], indirect=True)
assert state.state == STATE_ON async def test_all_day_event_returned_mid(hass, calendar, set_tz):
assert dict(state.attributes) == { """Test that the event lasting the whole day is returned, if it's in the middle of the local day."""
"friendly_name": "Private", config = dict(CALDAV_CONFIG)
"message": "This is an all day event", config["custom_calendars"] = [
"all_day": True, {"name": "Private", "calendar": "Private", "search": ".*"}
"offset_reached": False, ]
"start_time": "2017-11-27 00:00:00",
"end_time": "2017-11-28 00:00:00", await _day_event_returned(
"location": "Hamburg", hass,
"description": "What a beautiful day", calendar,
} config,
datetime.datetime(2017, 11, 27, 12, 30).replace(tzinfo=dt.DEFAULT_TIME_ZONE),
)
@pytest.mark.parametrize("set_tz", ["utc", "new_york", "baghdad"], indirect=True)
async def test_all_day_event_returned_late(hass, calendar, set_tz):
"""Test that the event lasting the whole day is returned, if it's late in the local day."""
config = dict(CALDAV_CONFIG)
config["custom_calendars"] = [
{"name": "Private", "calendar": "Private", "search": ".*"}
]
await _day_event_returned(
hass,
calendar,
config,
datetime.datetime(2017, 11, 27, 23, 30).replace(tzinfo=dt.DEFAULT_TIME_ZONE),
)
@patch("homeassistant.util.dt.now", return_value=_local_datetime(21, 45)) @patch("homeassistant.util.dt.now", return_value=_local_datetime(21, 45))
@ -655,33 +730,72 @@ async def test_event_rrule_endless(mock_now, hass, calendar):
} }
@patch( async def _event_rrule_all_day(hass, calendar, config, date_time):
"homeassistant.util.dt.now", with patch("homeassistant.util.dt.now", return_value=date_time):
return_value=dt.as_local(datetime.datetime(2016, 12, 1, 17, 30)), assert await async_setup_component(hass, "calendar", {"calendar": config})
) await hass.async_block_till_done()
async def test_event_rrule_all_day(mock_now, hass, calendar):
"""Test that the recurring all day event is returned.""" state = hass.states.get("calendar.private_private")
assert state.name == calendar.name
assert state.state == STATE_ON
assert dict(state.attributes) == {
"friendly_name": "Private",
"message": "This is a recurring all day event",
"all_day": True,
"offset_reached": False,
"start_time": "2016-12-01 00:00:00",
"end_time": "2016-12-02 00:00:00",
"location": "Hamburg",
"description": "Groundhog Day",
}
@pytest.mark.parametrize("set_tz", ["utc", "new_york", "baghdad"], indirect=True)
async def test_event_rrule_all_day_early(hass, calendar, set_tz):
"""Test that the recurring all day event is returned early in the local day, and not on the first occurrence."""
config = dict(CALDAV_CONFIG) config = dict(CALDAV_CONFIG)
config["custom_calendars"] = [ config["custom_calendars"] = [
{"name": "Private", "calendar": "Private", "search": ".*"} {"name": "Private", "calendar": "Private", "search": ".*"}
] ]
assert await async_setup_component(hass, "calendar", {"calendar": config}) await _event_rrule_all_day(
await hass.async_block_till_done() hass,
calendar,
config,
datetime.datetime(2016, 12, 1, 0, 30).replace(tzinfo=dt.DEFAULT_TIME_ZONE),
)
state = hass.states.get("calendar.private_private")
assert state.name == calendar.name @pytest.mark.parametrize("set_tz", ["utc", "new_york", "baghdad"], indirect=True)
assert state.state == STATE_ON async def test_event_rrule_all_day_mid(hass, calendar, set_tz):
assert dict(state.attributes) == { """Test that the recurring all day event is returned in the middle of the local day, and not on the first occurrence."""
"friendly_name": "Private", config = dict(CALDAV_CONFIG)
"message": "This is a recurring all day event", config["custom_calendars"] = [
"all_day": True, {"name": "Private", "calendar": "Private", "search": ".*"}
"offset_reached": False, ]
"start_time": "2016-12-01 00:00:00",
"end_time": "2016-12-02 00:00:00", await _event_rrule_all_day(
"location": "Hamburg", hass,
"description": "Groundhog Day", calendar,
} config,
datetime.datetime(2016, 12, 1, 17, 30).replace(tzinfo=dt.DEFAULT_TIME_ZONE),
)
@pytest.mark.parametrize("set_tz", ["utc", "new_york", "baghdad"], indirect=True)
async def test_event_rrule_all_day_late(hass, calendar, set_tz):
"""Test that the recurring all day event is returned late in the local day, and not on the first occurrence."""
config = dict(CALDAV_CONFIG)
config["custom_calendars"] = [
{"name": "Private", "calendar": "Private", "search": ".*"}
]
await _event_rrule_all_day(
hass,
calendar,
config,
datetime.datetime(2016, 12, 1, 23, 30).replace(tzinfo=dt.DEFAULT_TIME_ZONE),
)
@patch( @patch(