Files
goodwe-addon/goodwe_controller/DECISIONS/SAFETY-123-hour-slices-step-in-utc.md
T
glenn schrooyenandClaude d2820e8b9b
checks / unit (pull_request) Successful in 48m40s
infra-pin / fresh (pull_request) Successful in 2s
SAFETY-123: the day plan's hour loop steps in UTC and always ends
dayplan._hour_slices() added an hour to the local wall clock and read it
back. Without tzdata (tariff._EUTimezone) 02:00 on the spring change reads
as 00:00 UTC again, so the loop never advanced and the list grew until the
process died. Each step now adds the time left in the local hour to the UTC
instant. Ordinary days give the old slices; the spring day gives 23 slices
and the autumn day 25. No option, no text change.

test_safety123.py: every quarter of 27-29 March and 30 Oct-1 Nov 2027 (and
the US change days) for the slices, dayplan.plan() and
Controller._mode_roles(), each call under a hard time limit.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
2026-10-27 08:58:54 +01:00

2.0 KiB

SAFETY-123 - the day plan's hour loop steps in UTC

Status: for review. Tests: test_safety123.py. No option, no text change.

dayplan._hour_slices() stepped to the next local hour by adding an hour to the local wall clock and converting back. Where the zone has no tzdata (tariff._EUTimezone: the CI image, a dev host, an image that loses tzdata) the missing 02:00 of the spring change reads as summer time, which is 00:00 UTC again. At 28 March 2027 00:00 UTC t did not move and the list grew until the process died. Control and the web page share one event loop, so the whole add-on froze. dayplan.plan() calls it on every plan, before it looks at a battery's mode, so a site with all batteries on Solar first reaches it as well, if the prices give a plan.

The shipped image has tzdata (amd64-base:3.20), where this zone gives the right answer, so a running add-on is not known to be hit. The fix is for any host without it. (AUDIT-01 F4, card SAFETY-120, is the same defect.)

Decision. Each step adds the time left in the local hour to the UTC instant. It is at least a microsecond, so t always advances and the loop ends, in any zone, with or without tzdata. A day of 23 hours gives 23 slices and a day of 25 hours gives 25, each at most an hour, none across a local hour. On ordinary days the slices are the old ones.

One change on the autumn day. The old function (with tzdata) made one two-hour slice for the doubled hour 02:00-03:00; it now makes two of an hour, both labelled 2. plan() sums per slice, so only a forecast with sun in that hour could tell the difference. There is none.

Other loops. The other code that walks local hours or days steps in UTC or on the calendar date (static_tariff, price_forecast, price_history, price, maintenance, homeview.local_day, solarfc._bounds), or has a fixed bound. None can stall. One known inexactness stays, outside this fix: solarfc spreads a state-only forecast over hours since local midnight, so on the two change days its sun curve is an hour off the clock.