Skip to content

fix(utils): compute midnight_utc with correct DST offset#3689

Open
bpinto wants to merge 1 commit intospringfall2008:mainfrom
bpinto:dst-fix
Open

fix(utils): compute midnight_utc with correct DST offset#3689
bpinto wants to merge 1 commit intospringfall2008:mainfrom
bpinto:dst-fix

Conversation

@bpinto
Copy link
Copy Markdown
Contributor

@bpinto bpinto commented Mar 29, 2026

Disclaimer: I'm no python programmer so I used AI, I asked it to create a function for generating a local_midnight function that would return a "midnight" with the current time TZ.

I haven't read it carefully enough to think about alternative solutions, but maybe there is a better solution if we remove midnight_utc.

P.S.: I haven't been able to test this code yet since I'm using the docker add-on and it mingles its own files with Predbat's, so I'm finding an alternative solution to test this. I've opened this PR in case it's useful, but please close it, if it's not the correct fix.


On DST transition days, datetime.replace(hour=0) preserves the current UTC offset instead of the offset in effect at midnight. For example on UK spring-forward day, now at 14:00+01:00 produces midnight as 00:00+01:00 (actually 23:00 UTC previous day) instead of the correct 00:00+00:00. This shifts every rate minute index by 60 minutes, breaking charge/discharge schedules.

Add a local_midnight() helper in utils.py that strips tzinfo and re-localises via pytz.localize(), which resolves the correct offset for the given date/time. For fixed-offset timezones (no DST) the simple replace() path is preserved.

Replace all broken .replace(hour=0) patterns across predbat.py, gecloud.py, fox.py, octopus.py, solis.py, predheat.py, and the test_execute.py mock with calls to local_midnight().

Add a DST-specific test in test_fetch_octopus_rates.py verifying that rates with mixed +00:00/+01:00 offsets land at the correct minute indices.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses incorrect “midnight” baselines on DST transition days (notably with pytz), which can shift minute-indexed rate/schedule calculations by 60 minutes and break planning/execution logic across Predbat and its integrations.

Changes:

  • Add utils.local_midnight() to compute a correctly-localized midnight for a given local date when using pytz.
  • Replace DST-problematic datetime.replace(hour=0, ...) midnight computations with local_midnight() across core Predbat and several integration mock bases.
  • Add a DST-focused rates test case and update execute-test inverter mock to use the new helper.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
apps/predbat/utils.py Adds local_midnight() helper intended to avoid pytz DST offset retention when computing midnight.
apps/predbat/predbat.py Uses local_midnight() in update_time() to compute the midnight baseline used for minute indexing.
apps/predbat/predheat.py Uses local_midnight() for midnight_utc baseline in heat prediction loop.
apps/predbat/octopus.py Updates MockBase midnight baseline to use local_midnight().
apps/predbat/gecloud.py Updates MockBase midnight baseline to use local_midnight().
apps/predbat/fox.py Updates MockBase midnight baseline to use local_midnight().
apps/predbat/solis.py Updates MockBase midnight baseline to use local_midnight().
apps/predbat/tests/test_execute.py Updates the execute test inverter mock to compute midnight via local_midnight() and threads TZ through.
apps/predbat/tests/test_fetch_octopus_rates.py Adds a DST mixed-offset rates test case targeting correct minute placement.

@bpinto
Copy link
Copy Markdown
Contributor Author

bpinto commented Mar 29, 2026

I'll be back home in a few hours to read the comments and review this more carefully. I should then be able to test these changes locally first.

Implement local_midnight(dt_aware) to derive midnight from the datetime's own timezone and use pytz re-localisation when needed, so DST-transition days get the correct midnight offset.

Update callers/tests accordingly and extend Octopus rate tests to cover both spring-forward and fall-back mixed-offset cases.
@bpinto
Copy link
Copy Markdown
Contributor Author

bpinto commented Mar 30, 2026

@springfall2008 I still couldn't test this, hopefully if you like my proposal #3703 I should be able to test these changes. Unfortunately I didn't have the time yesterday to debug this as it would have been better to test this during the DST changing day.

P.S.: I haven't changed the variable naming but the _utc suffix isn't accurate since those are using the local timezone which may or may not be UTC timezone. I could change on this PR if you are okay, I didn't change it because that would increase the diff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants