Upstream 9f9f16b3 and 872eaecf. Saham, Gulika, and Shayanadi opt into Hindu rising. The default window used by rectification and Panchanga stays the apparent-disk one. BUG-1388. Co-Authored-By: Grok (agent loop) Co-authored-by: Cursor <cursoragent@cursor.com>
77 lines
3.4 KiB
Python
77 lines
3.4 KiB
Python
from datetime import datetime
|
|
|
|
import pytest
|
|
import swisseph as swe
|
|
|
|
from scripts.saham_daynight import determine_daytime
|
|
|
|
|
|
def test_swiss_daynight_does_not_use_solar_house_proxy():
|
|
noon = determine_daytime(datetime(2026, 7, 12, 12, 0), lat=39.9042, lon=116.4074, tz=8)
|
|
midnight = determine_daytime(datetime(2026, 7, 12, 0, 0), lat=39.9042, lon=116.4074, tz=8)
|
|
|
|
assert noon["status"] == "computed"
|
|
assert noon["is_daytime"] is True
|
|
assert midnight["is_daytime"] is False
|
|
assert noon["method"] == "swisseph.rise_trans"
|
|
|
|
|
|
def _legacy_apparent_daytime(moment, *, lat, lon, tz):
|
|
"""The pre-T10 window. Rectification and Panchanga still call this default."""
|
|
local = moment.replace(tzinfo=None)
|
|
jd = swe.julday(local.year, local.month, local.day, local.hour + local.minute / 60 + local.second / 3600 - float(tz))
|
|
geopos = (float(lon), float(lat), 0.0)
|
|
rise_status, rise = swe.rise_trans(jd - 1.0, swe.SUN, swe.CALC_RISE, geopos)
|
|
set_status, sunset = swe.rise_trans(jd - 1.0, swe.SUN, swe.CALC_SET, geopos)
|
|
assert rise_status == 0 and set_status == 0
|
|
sunrise_jd, sunset_jd = rise[0], sunset[0]
|
|
while sunrise_jd > jd:
|
|
sunrise_jd -= 1.0
|
|
while sunset_jd > jd:
|
|
sunset_jd -= 1.0
|
|
is_day = sunrise_jd <= jd < sunset_jd if sunrise_jd < sunset_jd else not (sunset_jd <= jd < sunrise_jd)
|
|
return {
|
|
"scope": "saham_daynight_swiss",
|
|
"status": "computed",
|
|
"is_daytime": is_day,
|
|
"julian_day_ut": jd,
|
|
"sunrise_jd_ut": sunrise_jd,
|
|
"sunset_jd_ut": sunset_jd,
|
|
"method": "swisseph.rise_trans",
|
|
"boundary": "Formula-specific +30 degree exceptions must be applied by the Saham rule layer, not inferred from house placement.",
|
|
}
|
|
|
|
|
|
def test_default_daytime_matches_rectification_apparent_sunrise():
|
|
# active_rectification_event_engine._sunrise_local_naive calls noon with no rising argument.
|
|
for date in ("2000-01-01", "2026-06-21", "2026-12-21"):
|
|
noon = datetime.fromisoformat(f"{date}T12:00:00")
|
|
current = determine_daytime(noon, lat=39.9042, lon=116.4074, tz=8)
|
|
previous = _legacy_apparent_daytime(noon, lat=39.9042, lon=116.4074, tz=8)
|
|
assert current == previous
|
|
assert "next_sunrise_jd_ut" not in current
|
|
|
|
|
|
@pytest.mark.parametrize("hour", [0, 4, 12, 23])
|
|
@pytest.mark.parametrize("month,day", [(3, 20), (6, 21), (9, 22), (12, 21)])
|
|
def test_hindu_consecutive_solar_events_are_recomputed_not_shifted(month, day, hour):
|
|
moment = datetime(2026, month, day, hour)
|
|
result = determine_daytime(moment, lat=39.9, lon=116.4, tz=8, rising="hindu")
|
|
rise = result["sunrise_jd_ut"]
|
|
flags = swe.FLG_SWIEPH | swe.FLG_TRUEPOS
|
|
year, month_num, day_num, _ = swe.revjul(rise + 8 / 24)
|
|
midnight = swe.julday(year, month_num, day_num, -8)
|
|
|
|
def event(start, kind):
|
|
status, times = swe.rise_trans(
|
|
start, swe.SUN, kind | swe.BIT_HINDU_RISING, (116.4, 39.9, 0), flags=flags,
|
|
)
|
|
assert status == 0
|
|
return times[0]
|
|
|
|
assert rise == pytest.approx(event(midnight, swe.CALC_RISE), abs=1e-8, rel=0)
|
|
assert result["sunset_jd_ut"] == pytest.approx(event(midnight, swe.CALC_SET), abs=1e-8, rel=0)
|
|
assert result["next_sunrise_jd_ut"] == pytest.approx(event(midnight + 1, swe.CALC_RISE), abs=1e-8, rel=0)
|
|
assert rise <= result["julian_day_ut"] < result["next_sunrise_jd_ut"]
|
|
assert result["method"] == "swisseph.rise_trans_hindu_rising"
|