From aafe6ff0e295e870a4a0b344a6880c97109d31b3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 29 Apr 2022 01:48:58 -0500 Subject: [PATCH] Fix history_stats for timezones with a positive offset from UTC (#71038) --- .../components/history_stats/data.py | 4 +- tests/components/history_stats/test_sensor.py | 81 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/history_stats/data.py b/homeassistant/components/history_stats/data.py index c5d0ee0fb15..3f22f4cc32b 100644 --- a/homeassistant/components/history_stats/data.py +++ b/homeassistant/components/history_stats/data.py @@ -11,6 +11,8 @@ import homeassistant.util.dt as dt_util from .helpers import async_calculate_period, floored_timestamp +MIN_TIME_UTC = datetime.datetime.min.replace(tzinfo=dt_util.UTC) + @dataclass class HistoryStatsState: @@ -36,7 +38,7 @@ class HistoryStats: """Init the history stats manager.""" self.hass = hass self.entity_id = entity_id - self._period = (datetime.datetime.min, datetime.datetime.min) + self._period = (MIN_TIME_UTC, MIN_TIME_UTC) self._state: HistoryStatsState = HistoryStatsState(None, None, self._period) self._history_current_period: list[State] = [] self._previous_run_before_start = False diff --git a/tests/components/history_stats/test_sensor.py b/tests/components/history_stats/test_sensor.py index 40a13116026..bfa0c8f415e 100644 --- a/tests/components/history_stats/test_sensor.py +++ b/tests/components/history_stats/test_sensor.py @@ -1306,3 +1306,84 @@ async def test_measure_from_end_going_backwards(hass, recorder_mock): assert hass.states.get("sensor.sensor2").state == "0.83" assert hass.states.get("sensor.sensor3").state == "1" assert hass.states.get("sensor.sensor4").state == "83.3" + + +async def test_measure_cet(hass, recorder_mock): + """Test the history statistics sensor measure with a non-UTC timezone.""" + hass.config.set_time_zone("Europe/Berlin") + start_time = dt_util.utcnow() - timedelta(minutes=60) + t0 = start_time + timedelta(minutes=20) + t1 = t0 + timedelta(minutes=10) + t2 = t1 + timedelta(minutes=10) + + # Start t0 t1 t2 End + # |--20min--|--20min--|--10min--|--10min--| + # |---off---|---on----|---off---|---on----| + + def _fake_states(*args, **kwargs): + return { + "binary_sensor.test_id": [ + ha.State("binary_sensor.test_id", "on", last_changed=t0), + ha.State("binary_sensor.test_id", "off", last_changed=t1), + ha.State("binary_sensor.test_id", "on", last_changed=t2), + ] + } + + await async_setup_component( + hass, + "sensor", + { + "sensor": [ + { + "platform": "history_stats", + "entity_id": "binary_sensor.test_id", + "name": "sensor1", + "state": "on", + "start": "{{ as_timestamp(now()) - 3600 }}", + "end": "{{ now() }}", + "type": "time", + }, + { + "platform": "history_stats", + "entity_id": "binary_sensor.test_id", + "name": "sensor2", + "state": "on", + "start": "{{ as_timestamp(now()) - 3600 }}", + "end": "{{ now() }}", + "type": "time", + }, + { + "platform": "history_stats", + "entity_id": "binary_sensor.test_id", + "name": "sensor3", + "state": "on", + "start": "{{ as_timestamp(now()) - 3600 }}", + "end": "{{ now() }}", + "type": "count", + }, + { + "platform": "history_stats", + "entity_id": "binary_sensor.test_id", + "name": "sensor4", + "state": "on", + "start": "{{ as_timestamp(now()) - 3600 }}", + "end": "{{ now() }}", + "type": "ratio", + }, + ] + }, + ) + await hass.async_block_till_done() + + with patch( + "homeassistant.components.recorder.history.state_changes_during_period", + _fake_states, + ): + for i in range(1, 5): + await async_update_entity(hass, f"sensor.sensor{i}") + await hass.async_block_till_done() + + assert hass.states.get("sensor.sensor1").state == "0.83" + assert hass.states.get("sensor.sensor2").state == "0.83" + assert hass.states.get("sensor.sensor3").state == "1" + assert hass.states.get("sensor.sensor4").state == "83.3"