Anchor derived solar midnight in the night it belongs to (#1599)

This commit is contained in:
SGXander 2026-09-17 21:55:22 +01:00
commit 7a14241c61
2 changed files with 38 additions and 2 deletions

View file

@ -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]]:

View file

@ -152,6 +152,40 @@ def test_noon_and_midnight(tzinfo_and_location):
assert noon == location.noon(date)
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