Panchang Engine — High-Level Design
Status: Draft for review · Part of: Astro Composer · Updated: 2026-10-24
The panchang engine turns one local day into five limbs — tithi (lunar day), vara (weekday), nakshatra (lunar mansion), yoga and karana (half-tithi) — each with exact start and end instants anchored to sunrise. It is pure arithmetic on binder positions and rise/set answers: no provider call, interpolation, timezone database or clock.
1. Purpose
Panchang is what users check against a printed almanac, so a convention error shows immediately. Four conventions matter:
- The day’s tithi is the one running at sunrise, not at midnight.
- Vara (the weekday) changes at sunrise.
- A tithi can be skipped (
kshaya) or repeat (adhika). - People expect an end time (“Panchami ends at 14:32”), not just a name.
The engine exists so that:
- one sunrise definition anchors the day, tithi and vara;
- every limb is an interval with exact boundary instants, not a sampled guess;
- kshaya and adhika tithis are never deduplicated;
- tithi and karana ignore ayanamsa while nakshatra and yoga inherit it;
- every response names its profile, rulebook and binder provenance.
2. Where it sits
binder rise-set + positions ──┐
request · local date + offset ─┼──▶ panchang engine ──▶ five limbs + boundary instants
observer · profile · rulebook ┘
- Upstream: the composition facade supplies the local date, UTC offset, observer, profile and one rulebook handle; the binder client supplies sunrise, next sunrise and Sun/Moon positions on request.
- Downstream: the facade builds
POST /v1/panchang; muhurta packs and calendar features reuse the same limb intervals. - Never: it does not call a provider, interpolate an ephemeris, read a clock, use a timezone database, or produce festival and muhurta rules.
Input contract
| Input | Source | Used for |
|---|---|---|
| Local date | Request | The sunrise-to-sunrise day to describe |
| UTC offset | Request | Resolving the local day to an absolute search window; no tz database |
| Observer | Request | Latitude, longitude and elevation for the Hindu sunrise |
| Profile | Request | Sunrise definition (C6), day boundary (C7), zodiac and ayanamsa |
| Rulebook handle | Registry | Limb names, tithi names, karana cycle, boundary tolerance |
| Rise/set answer | Binder POST /v1/rise-set |
Sunrise, next sunrise, optional sunset |
| Sun and Moon positions | Binder POST /v1/positions |
Longitude, speed, sign and nakshatra id per probe |
| Boundary probes | Facade through the binder client | Additional instants requested by the solver |
Output contract
| Output | Shape | Read by |
|---|---|---|
day |
Local date, offset, sunrise, next sunrise, optional sunset | Response, muhurta packs |
atSunrise |
Tithi, vara, nakshatra, yoga and karana at the sunrise instant | Response, daily summary |
limbs.tithi[] |
Index, name, paksha, start, end, kshaya, adhika |
Response, muhurta filters |
limbs.nakshatra[] |
Index, name, start, end | Response, muhurta filters |
limbs.yoga[] |
Index, name, start, end | Response, muhurta filters |
limbs.karana[] |
Index, name, movable or fixed, start, end | Response, muhurta filters |
vara |
Canonical weekday id, weekday index, start, end | Response, muhurta filters |
boundaryPrecision |
Tolerance, probe count, convergence state | Audit and fixtures |
diagnostics |
No sunrise, missing next sunrise, unavailable sunset | Facade degradation and errors |
3. Responsibilities
Each area owns one decision and one deterministic rule.
| Area | Owns | Deterministic rule |
|---|---|---|
| Day anchor | Sunrise, next sunrise and the day interval | Day is [sunrise, nextSunrise); vara changes at sunrise |
| Tithi | 12° elongation intervals and names | floor(elongation / 12) + 1, 1–30; half-open |
| Vara | Weekday of the sunrise instant | Derived from sunrise, never from civil midnight |
| Nakshatra | Moon nakshatra intervals | Binder id is authority; engine only finds change instants |
| Yoga | 13°20′ of (sun + moon) mod 360 |
floor(((sun + moon) mod 360) / 13.3333…) |
| Karana | 6° elongation intervals, 60 per lunation | Rulebook cycle of seven movable and four fixed names |
| Boundary solve | Crossing instant for each limb | Bracketed root with a Newton step on binder speed, ≤ 1 s |
| Tithi flags | Kshaya, adhika and at-sunrise state | Derived from which sunrises the interval contains |
| Assembly | Five limbs in a fixed order | Canonical ids, UTC instants, one rounding point |
4. Architecture
Diagram: reference design, not deployed behaviour.
The engine is a pure module with one seam: a boundary probe port. The engine picks the next instant it needs; the facade runs the binder read. That keeps the solver deterministic and testable without a network client, cache or clock.
5. Rules
- Sunrise defines the day. Day tithi and vara are the ones at sunrise; midnight or noon boundaries are a different product and are not served.
- Five limbs, two quantities. Tithi and karana use Sun–Moon elongation, nakshatra the sidereal Moon, yoga the sidereal Sun-plus-Moon sum, vara the sunrise instant. No other body participates.
- The binder owns nakshatra. The engine reads its nakshatra id per probe and may use the in-segment remainder as a Newton step, never deriving an id from a longitude.
- Tithi and karana ignore ayanamsa. Both are longitude differences, so a profile change cannot move them. Sidereal nakshatra and yoga do move; the response names the profile.
- Boundaries are solved, not sampled. Every limb carries start and end instants; missing one second fails the request.
- No ephemeris interpolation. The probe port returns longitude and speed and the engine only does arithmetic; the facade owns every binder read.
- Half-open intervals. A limb beginning exactly at a boundary belongs to the new interval; one beginning at sunrise opens the day.
- Kshaya and adhika are reported. A tithi with no sunrise inside is
kshaya; one holding both the day’s sunrise and the next isadhika. Neither is removed, merged or renumbered. - Karana is a rulebook cycle. Seven movable names repeat eight times per lunation; the first half of Shukla Pratipada and the last three half-tithis are fixed. Names and positions come from the rulebook.
- Vara is sunrise to sunrise. The id is the Sanskrit weekday (
somavara) plus an index; display names belong to the client. - No timezone database, no clock. The caller supplies the local date and offset; the engine returns UTC instants and never resolves DST, reads the server clock or formats a date.
- No partial panchang. Missing sunrise, missing next sunrise or a non-converged boundary fails the request;
allowPartialdoes not cover the five limbs. A missing sunset isnull. - The probe budget is a contract. Each boundary gets a bounded probe count; exhausting it is
LIMIT_EXCEEDED, never an approximate boundary. - Deterministic order. Limbs are emitted tithi, vara, nakshatra, yoga, karana, intervals ordered by start. Two identical requests give byte-identical output.
6. Edge cases
Bad input fails loudly; nothing is silently approximated.
| Case | Behaviour |
|---|---|
| Polar day: no sunrise on the local date | 422 LAYER_UNSUPPORTED with reason: "no_sunrise"; no midnight fallback |
| Polar night: sunrise exists but no next sunrise | Same failure; no fabricated 24-hour day |
| Tithi begins after sunrise and ends before next sunrise | Included with kshaya: true; not removed from the day list |
| Same tithi at two consecutive sunrises | adhika: true; one interval spans both sunrises |
| Tithi ends exactly at sunrise | Half-open rule makes the new tithi the day’s tithi |
| Nakshatra changes exactly at sunrise | Binder id at sunrise decides; boundary instant is sunrise |
| Two karana boundaries in one day | Both intervals returned in order, never collapsed |
| Karana interval crosses the next sunrise | Ends at the solved boundary; the next day owns the remainder |
| Elongation or Moon longitude at 359.9999° | Wraps correctly; intervals are never negative or reordered |
| Ayanamsa changes at a nakshatra boundary | Nakshatra and yoga move; tithi and karana do not |
| DST transition in the supplied offset | Caller resolves the offset; engine uses the supplied offset and no tz database |
| Binder omits speed for a probe | Solver falls back to bracketed bisection within the same probe budget |
| Boundary solver exhausts its probe budget | LIMIT_EXCEEDED; no approximate boundary in a success response |
| Binder degraded to a lower-precision engine | Panchang still returned; degraded propagates in binder metadata |
| Sunset unavailable but sunrise present | Panchang returned; sunset: null; limbs unaffected |
| Local date skipped by a civil calendar change | Caller’s offset defines the instant; the engine does not invent a date |
| Leap second near a boundary | Instants remain UTC; the one-second tolerance absorbs the discontinuity |
allowPartial requested |
Ignored for the five limbs; missing sunrise still fails |
| Very short or very long day at high latitude | Sunrise and next sunrise still define the day; no minimum day length |
7. Out of scope
- Muhurta windows, Rahu kala, Yamaganda, Gulika, panchaka, Abhijit, hora and choghadiya (muhurta packs).
- Festival names, samvat, regional month names and calendar conversion.
- Sunrise, sunset and horizon astronomy (Astro Binder).
- Eclipses, upagraha, planetary war, transits and dasha periods.
- Alternative day boundaries at midnight or noon.
- Timezone database and DST resolution (caller and facade).
- Interpretation, remedies and almanac prose (product content).
- Caching and provenance storage (facade, result cache and provenance store).
- Matching, transits and natal chart assembly (chart core).
8. Planned code
| Piece | Responsibility |
|---|---|
panchang/PanchangEngine |
Admission, probe planning and five-limb assembly |
panchang/SunriseAnchor |
Hindu sunrise, next sunrise and vara boundary |
panchang/BoundaryProbePort |
Instant probe contract executed by the facade |
panchang/LimbBoundarySolver |
Bracketed root, Newton step, tolerance and budget |
panchang/TithiResolver |
12° intervals, paksha, names, kshaya and adhika flags |
panchang/NakshatraYogaResolver |
Binder nakshatra changes and yoga intervals |
panchang/KaranaCycle |
60-karana rulebook cycle, names and movable/fixed state |
panchang/VaraResolver |
Sunrise weekday id, index and interval |
panchang/PanchangAssembler |
Ordered intervals, at-sunrise values and rounding |