#!/usr/bin/env python3 # This code is free and released into the public domain. # (a) The Gregorian day on which Sankranti occurs is counted as Day 1 of that Sayana month # (b) Sayana day runs from midnight-to-midnight (UTC, 24 hours), just like Gregorian day. # Does not depend on location because, polar locations might not have one import datetime from skyfield.api import load from skyfield.searchlib import find_discrete # Load ephemeris ts = load.timescale() # de441 = 3.1GB for years -13200 to +17191 # de422 = 623MB for years -3000 to +3000 # de405 = 63MB for years 1600 to 2200 # de421 = 17MB for years 1900 to 2050 eph = load('de421.bsp') earth, sun = eph['earth'], eph['sun'] rasis_sayana = [ "Mesha", "Vrishabha", "Mithuna", "Karka", "Simha", "Kanya", "Tula", "Vrishchika", "Dhanus", "Makara", "Kumbha", "Meena" ] def gregorian_to_sayana_utc_midnight(year, month, day, hour=0, minute=0, second=0): target_t = ts.utc(year, month, day, hour, minute, second) # 1. Ecliptic Longitude (Sayana) apparent = earth.at(target_t).observe(sun).apparent() _, lon, _ = apparent.ecliptic_latlon(epoch='date') current_deg = lon.degrees month_idx = int(current_deg // 30) # 2. Find previous Sankranti t0_datetime = target_t.utc_datetime() - datetime.timedelta(days=32) t0 = ts.utc(t0_datetime.year, t0_datetime.month, t0_datetime.day) def sankranti_state(t): app = earth.at(t).observe(sun).apparent() _, l, _ = app.ecliptic_latlon(epoch='date') return (l.degrees // 30).astype(int) sankranti_state.step_days = 1.0 times, states = find_discrete(t0, target_t, sankranti_state) if len(times) == 0: return "Error: Could not locate previous Sankranti." sankranti_time = times[-1] # 3. Calculate Days using UTC Midnight boundaries sankranti_dt = sankranti_time.utc_datetime().date() target_dt = target_t.utc_datetime().date() # Calculate the difference in simple calendar days delta_days = (target_dt - sankranti_dt).days sayana_day = delta_days + 1 return { "Gregorian Target (UTC)": target_t.utc_strftime('%Y-%m-%d %H:%M:%S UTC'), "Previous Sankranti (UTC)": sankranti_time.utc_strftime('%Y-%m-%d %H:%M:%S UTC'), "Sayana Month": rasis_sayana[month_idx], "Sayana Day": sayana_day, "Degrees": round(current_deg, 4) } def sayana_to_gregorian_utc(gregorian_year, sayana_month_name, sayana_day): if sayana_month_name not in rasis_sayana: return "Error: Invalid Sayana month name." target_month_idx = rasis_sayana.index(sayana_month_name) # 1. Search window for the Sankranti # Extending the search into the next year to catch late winter months (like Meena) t0 = ts.utc(gregorian_year - 1, 12, 1) t1 = ts.utc(gregorian_year + 1, 4, 1) def sankranti_state(t): app = earth.at(t).observe(sun).apparent() _, l, _ = app.ecliptic_latlon(epoch='date') return (l.degrees // 30).astype(int) sankranti_state.step_days = 15.0 # 15 days is mathematically safe for 30° jumps times, states = find_discrete(t0, t1, sankranti_state) # 2. Locate the specific Sankranti for the target month sankranti_time = None for time, state in zip(times, states): if state == target_month_idx and time.utc_datetime().year == gregorian_year: sankranti_time = time break # Fallback if the month spans the year boundary (e.g., Makara starting in Dec) if sankranti_time is None: for time, state in zip(times, states): if state == target_month_idx: sankranti_time = time break if sankranti_time is None: return "Error: Could not locate Sankranti." # 3. Calculate target Gregorian Date # Day 1 is the UTC date of the Sankranti sankranti_dt = sankranti_time.utc_datetime().replace(tzinfo=datetime.timezone.utc) start_date = sankranti_dt.date() # Add offset (Sayana Day - 1) target_date = start_date + datetime.timedelta(days=(sayana_day - 1)) return { "Sayana Input": f"{sayana_month_name} {sayana_day}", "Sankranti (UTC)": sankranti_dt.strftime('%Y-%m-%d %H:%M:%S UTC'), "Gregorian Date (UTC)": target_date.strftime('%Y-%m-%d') } if __name__ == "__main__": result = gregorian_to_sayana_utc_midnight(2026, 3, 19, 6, 0, 0) for key, value in result.items(): print(f"{key}: {value}") # Find the Gregorian date for the 15th day of Mesha in 2026 result = sayana_to_gregorian_utc(2026, "Mesha", 15) for key, value in result.items(): print(f"{key}: {value}")