From 12323cc357e0483cb579ab2db351116a055bff83 Mon Sep 17 00:00:00 2001 From: SGXander <8619536+SGXander@users.noreply.github.com> Date: Wed, 23 Sep 2026 00:03:07 +0100 Subject: [PATCH] Fix missing solar_midnight event when derived solar noon crosses 12:00 UTC (#1599) (#1609) * Anchor derived solar midnight in the night it belongs to (#1599) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: SGXander Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../adaptive_lighting/color_and_brightness.py | 6 ++-- tests/test_color_and_brightness.py | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/custom_components/adaptive_lighting/color_and_brightness.py b/custom_components/adaptive_lighting/color_and_brightness.py index e62beb0c..4645f994 100644 --- a/custom_components/adaptive_lighting/color_and_brightness.py +++ b/custom_components/adaptive_lighting/color_and_brightness.py @@ -193,10 +193,12 @@ class SunEvents: middle = abs(sunset - sunrise) / 2 if sunset > sunrise: noon = sunrise + middle - midnight = noon + timedelta(hours=12) * (1 if noon.hour < 12 else -1) + next_sunrise = self.sunrise(dt + timedelta(days=1)) + midnight = sunset + (next_sunrise - sunset) / 2 else: midnight = sunset + middle - noon = midnight + timedelta(hours=12) * (1 if midnight.hour < 12 else -1) + next_sunset = self.sunset(dt + timedelta(days=1)) + noon = sunrise + (next_sunset - sunrise) / 2 return noon, midnight def sun_events(self, dt: datetime.datetime) -> list[tuple[SunEvent, float]]: diff --git a/tests/test_color_and_brightness.py b/tests/test_color_and_brightness.py index e7f0a21a..8598b121 100644 --- a/tests/test_color_and_brightness.py +++ b/tests/test_color_and_brightness.py @@ -153,6 +153,41 @@ def test_noon_and_midnight(tzinfo_and_location): assert midnight == location.midnight(date) +@pytest.mark.parametrize("day", [dt.date(2026, 4, 17), dt.date(2026, 8, 30)]) +def test_midnight_falls_in_the_night_it_belongs_to(day): + """Derived midnight must lie between sunset and the next sunrise (#1599).""" + location = Location( + LocationInfo( + name="name", + region="region", + timezone="UTC", + latitude=51.5, + longitude=0.0, # on the prime meridian + ), + ) + sun_events = SunEvents( + name="test", + astral_observer=location.observer, + sunrise_time=None, + min_sunrise_time=None, + max_sunrise_time=dt.time(8, 0), + sunset_time=None, + min_sunset_time=dt.time(17, 0), + max_sunset_time=dt.time(20, 0), + timezone=zoneinfo.ZoneInfo("UTC"), + ) + date = dt.datetime.combine(day, dt.time(12), tzinfo=dt.UTC) + sunset = sun_events.sunset(date) + next_sunrise = sun_events.sunrise(date + dt.timedelta(days=1)) + _, midnight = sun_events.noon_and_midnight(date) + assert sunset < midnight < next_sunrise + + just_after_sunset = sunset + dt.timedelta(minutes=40) + events = sun_events.prev_and_next_events(just_after_sunset) + assert [event for event, _ in events] == [SunEvent.SUNSET, SunEvent.MIDNIGHT] + assert sun_events.sun_position(just_after_sunset) > -0.5 + + def test_sun_events(tzinfo_and_location): tzinfo, location = tzinfo_and_location sun_events = SunEvents(