# Real-date validation — 15 October 2026

## Runtime conventions

Astronomy Engine 2.1.19 (MIT), copied unmodified from its npm ESM distribution, runs entirely in the browser. One UTC epoch millisecond drives all displayed body directions and labels. UTC is converted to TT explicitly, using the published TAI−UTC table plus 32.184 seconds. The library’s approximate TT-to-UT conversion is not exposed as the user’s civil clock.

The Sun is the library’s apparent geocentric vector with light time and aberration. The Moon is `GeoMoon` at the one-step emission time determined by geocentric distance, rotated at observation time. Both use the library’s true ecliptic of date, IAU 2006 precession / IAU 2000B nutation. The planar diagram omits latitude visually; calculated phase illumination still includes the library’s three-dimensional geometry.

Conventional Lahiri uses the published J2000 **mean** anchor 23°51′25.5324″. The mean equinox direction of date is transported into the J2000 mean ecliptic by full coordinate rotations. Its longitude supplies accumulated precession. Nutation is added for the true ayanāṃśa subtracted from the true tropical longitudes. This method is validated against `SE_SIDM_LAHIRI = 1`, and does not silently substitute True Citra or a linear drift.

Tithi depends on directed Sun–Moon longitude difference; subtracting the same ayanāṃśa cancels in that difference. Nakshatra and pada use Moon sidereal longitude. Boundary solving unwraps the monotonic metric near the current instant, brackets each crossing and returns the first millisecond in the new interval. Millisecond numerical resolution does not imply equivalent astronomical accuracy. UI times are displayed to the minute.

## Independent fixtures and observed errors

`reference-oracle.py` was run against independently installed PySwissEph 2.10.3.2 / Swiss Ephemeris 2.10.03, using Moshier ephemeris, default apparent geocentric corrections and conventional Lahiri. **No Swiss code or binary is bundled or used at runtime.** The Python script is our own oracle adapter; generated numeric fixtures preserve the reference results.

There are **4,510 distinct position samples**: the original 528 quarter-start/monthly anchors, midpoints of every month from 1972 through 2100, every day of 2026 at varying times, 2,048 deterministic random instants (seed 20261015), and 34 clock/calendar edge instants, with duplicates removed. `extended-ephemeris-oracle.py` generates the expanded fixture independently with Swiss; the original smaller fixtures remain as regression checks. The oracle uses `utc_to_jd` through 2026. After 2026, both engines explicitly assume TT = UTC + 69.184 seconds. This avoids Swiss’s distant-future UT1 fallback being mistaken for a UTC comparison. Future leap seconds remain unknown.

| Quantity | Maximum observed difference |
| --- | ---: |
| Mean Lahiri ayanāṃśa | 0.000162917 arcsecond |
| Sun tropical longitude | 2.267332 arcseconds |
| Moon tropical longitude | 3.618799 arcseconds |
| Moon sidereal longitude | 3.540426 arcseconds |

At **562 anchors across 1972–2100**, the oracle separately solves tithi, nakshatra and pada intervals. Their **3,372 endpoints** agree within **7.750 seconds for tithi**, **6.432 seconds for nakshatra** and **6.006 seconds for pada**. The original gates were retained without relaxation: Sun <3″, Moon tropical/sidereal <4″, mean Lahiri <0.002″, and endpoints <15 seconds. Tests require the same interval index and less than 15 seconds at each endpoint; this regression threshold is not a claimed global accuracy bound.

A second source uses JPL Horizons **quantity 45**, apparent inertial RA/DEC, at geocenter (`500@399`), Sun body 10 and Moon body 301. Five samples per body cover 15–16 October 2026 at six-hour steps. The returned inertial vectors are rotated with the runtime’s ecliptic transformation so the comparison does not mix Horizons quantity 31’s IAU76/80 frame with the runtime frame. Maximum discrepancies are **1.535786 arcseconds for the Sun** and **2.282130 arcseconds for the Moon**. This checks independent body directions but shares a frame rotation. ICRF versus J2000 dynamical frame bias is below approximately 0.02″ and is not separately corrected here.

Full requests and responses are preserved in `scripts/fetch-horizons-reference.py` and `test/fixtures/jpl-horizons-{sun,moon}.txt`. HTTPS retrieval used system curl with TLS verification enabled. The offline report is reproduced with:

```sh
node scripts/validation-report.mjs
```

This writes [validation-results.json](validation-results.json), including maximum-error dates and limitations. To regenerate Swiss fixtures, install the pinned PySwissEph version in an isolated validation environment and run `scripts/reference-oracle.py`. That optional validation dependency has separate Swiss licensing; do not copy it into the application or redistribute it as a runtime dependency.

## Official Rashtriya Panchang follow-up

The alternate [official English listing](https://packolkata.imd.gov.in/rashtriya-panchang-english.php) and its [2026–27 archive](https://packolkata.imd.gov.in/download/EnglishRP2627.zip) were successfully downloaded over normal HTTPS with TLS verification. The web reader and older archives had timed out; no access denial or security check was bypassed. The archive contains `RP 1948 SE Final.pdf`, Saka Era 1948, published by IMD Positional Astronomy Centre in 2025. The fixture stores SHA-256 hashes and exact page references; the full copyrighted publication is not bundled.

Its explanatory pages specify geocentric **ending** instants in IST (UTC+05:30), a sunrise-based day grouping, and hours over 24 for the following Gregorian date. The variable ayanamsa reference is described as 23°15′00″ at 0h on 21 March 1956. Ahoratra denotes an interval that does not end between that day’s sunrise and the next. The comparison respects those conventions instead of interpreting 27:26 as a same-civil-day clock time.

Fourteen endpoints (8 tithi, 6 nakshatra) were transcribed from printed pages 4, 23, 53, 54, 58 and 60, across 2 April, 15 June, 10/15/16/26 October and 1 November 2026. All interval indices agree; all calculated endings, **rounded to the nearest minute for this comparison**, match the printed minute. Maximum absolute difference from the printed timestamp is **28.339 seconds**, within its half-minute rounding interval. This is agreement with minute-resolution data, not a 28-second accuracy guarantee. UI timestamps use ordinary local-clock formatting; this comparison rounds the underlying engine instants explicitly.

The 15 October Jyestha ahoratra observation also agrees: the calculated interval spans both published central-station sunrise times, 15 October 05:57 IST and 16 October 05:58 IST, before ending at approximately 06:47:52 IST on 16 October (printed 06:48). This uses published sunrise anchors only; no sunrise engine was added.

Numeric facts and provenance are preserved in `test/fixtures/imd-panchang-2026.json`; per-endpoint calculations are in `validation-results.json`. The additional regression test passes. These selected checks do not validate all daily rows, festivals, rise/set values, regional calendars or the full Panchang.

## Civil-time and interaction validation

[Systematic coverage](SYSTEMATIC-TESTING.md) documents the full timezone matrix, independent Python/IANA oracle, actual-browser run, classification margins, root traversal, inverse dragging and UI checks. The complete suite now passes 67 tests; 33 exercise actual UI handlers. There were no mathematical/reference failures in the expanded runs. One new assertion was corrected to accept the runtime’s canonical IANA alias; formatter validation was moved behind its cache to remove redundant work.


Automated tests exercise America/New_York DST gaps/folds, Australia/Lord_Howe half-hour transitions, Pacific/Apia’s skipped civil date, fractional offsets, leap years, invalid dates and timezone strings. Civil-month boundaries are resolved in the selected IANA zone rather than by assuming a constant UTC offset.

The inverse lunar-position solver samples a civil month at six-hour intervals, unwraps the Moon’s monotonically increasing sidereal longitude, and finds all matching occurrences. A tap chooses the nearest; a second pass is offered explicitly. Dragging accumulates signed angle changes from its starting circuit and clamps at the month edge instead of jumping to another pass. Tests cover both sides of 360°, repeated longitudes, reverse direction and month limits. No teaching offsets are transferred to real mode.

Integrated DOM tests verify all UI views share the same instant, exact transition attributes match the engine, date/timezone-only edits preserve the instant, DST ambiguity is explicit, invalid edits do not mutate active state, and teaching exercises restore independent geometric behavior.

## Boundaries of the claim

- Finite sample agreement is not proof of accuracy at every instant in 1972–2100. Astronomy Engine targets approximately arcminute accuracy; observed smaller discrepancies here are not a stronger library guarantee.
- After 2026 the current leap offset is retained. New announced leap seconds require a table update. JavaScript does not accept a `23:59:60` input.
- The browser’s IANA database governs historical and future civil-zone formatting. Future political timezone changes may require a browser update.
- A transition printed at a particular minute should be understood as an estimate, especially close to an interval edge. Classification can differ between ephemerides within their timing uncertainty.
- Official Panchang validation is limited to 14 published endpoints and one ahoratra observation; no full calendar/festival validation is claimed.
- Sunrise-based daily tithi/nakshatra assignment, local parallax/refraction, rise/set, eclipse and festival rules are outside scope. Optional place labels do not change global geocentric results.

Primary sources are linked in the prototype’s explanatory notes and README. The complete geometric teaching mode remains available independently of these astronomical calculations.
