- Dharmagya
- panchang docs
- Upgrading 4.x → 5
Upgrading 4.x → 5
panchang · v5.4.0 · MIT
Real Date instants and their *Local companions, the grouped result object, the corrected Lahiri ayanamsa, measured ΔT, and the read*/compute* naming convention.
Three changes move numbers that 4.x produced, and one option is gone. Everything else is renaming, and every rename is listed below.
Published Dates are real instants
- result.sunrise.getTime() // NOT when sunrise happened (off by the UTC offset)
- result.sunrise.getUTCHours() // the documented 4.x idiom
+ result.sun.rise.getTime() // correct epoch ms
+ result.sun.riseLocal // "2025-01-14T07:09:44.172+05:30"In 4.x every published Date was the true instant shifted by the UTC offset, and the docs told you to read it back with getUTC*. That worked only if you did nothing else with the value. JSON.stringify wrote a wrong instant labelled Z. Intl with a timeZone rendered 12:39 pm for an 07:09 am sunrise. Any comparison, diff, database write, date-fns call or Temporal call was off by the offset.
v5 fixes every Date in DailyPanchangResult and every TimePeriod: sun and moon events, all muhurtas and inauspicious periods, all four slot systems, the element startTime/endTime arrays, the eclipse contacts and the sutak window. Each one gains a *Local string beside it for display.
| You had | You now write |
|---|---|
r.sun.rise.getUTCHours() | r.sun.riseLocal.slice(11, 13) |
`${h}:${m}` from getUTC* | r.sun.riseLocal.slice(11, 16) |
r.inauspicious.rahuKalam.start.getUTCHours() | r.inauspicious.rahuKalam.startLocal.slice(11, 13) |
r.angas.tithis[0].endTime for display | r.angas.tithis[0].endTimeLocal |
a Date you derived yourself | formatInZone(d, r.timezone.offsetMinutes) |
getInstantPanchang results carry no *Local fields. That call takes no timezone, so there is no zone to render a wall clock in.
Building those strings costs about 0.04 ms per daily panchang. A warm call still finishes in 0.17 ms, against 6.20 ms for the published 4.3.1.
The result object is grouped
DailyPanchangResult had roughly 50 flat top-level fields mixing five categories. v5 sorts them into seven groups. See the group table for the shape. This is a large break, and it ships in the same release as the Date change on purpose: migrating both at once is one pass over your read sites instead of two.
Every rename, in full:
| 4.x | v5 |
|---|---|
sunrise / sunset / nextSunrise | sun.rise / sun.set / sun.nextRise |
sunriseLocal / sunsetLocal / nextSunriseLocal | sun.riseLocal / sun.setLocal / sun.nextRiseLocal |
dayDurationMinutes / nightDurationMinutes | sun.dayDurationMinutes / sun.nightDurationMinutes |
dinamanaMinutes / ratrimanaMinutes | sun.dinamanaMinutes / sun.ratrimanaMinutes |
siderealSunAtSunrise | sun.siderealLongitude |
suryaNakshatra | sun.nakshatra |
moonrise / moonset | moon.rise / moon.set |
moonriseLocal / moonsetLocal | moon.riseLocal / moon.setLocal |
siderealMoonAtSunrise | moon.siderealLongitude |
chandraRashi | moon.rashi |
tithis / nakshatras / yogas / karanas / vara | angas.* (same names) |
masa / chandramasa / samvat | calendar.* (same names) |
abhijitMuhurta / brahmaMuhurta / vijayaMuhurta / godhuliMuhurta / nishitaMuhurta | muhurtas.abhijit / .brahma / .vijaya / .godhuli / .nishita |
amritKala / madhyahna / pratahSandhya / sayahnaSandhya | muhurtas.* (same names) |
doGhatiMuhurta | muhurtas.doGhati |
rahuKalam / gulikaKalam / yamaganda / durMuhurta / varjyam / bhadra / gandaMula / panchaka / panchakaRahita | inauspicious.* (same names) |
choghadiya / hora | periods.choghadiya / periods.hora |
gowriPanchangam | periods.gowri |
eclipse.magnitude (disc area) | eclipse.obscuration holds that same value. The new eclipse.magnitude is the diameter fraction catalogues publish, and is negative for a penumbral lunar eclipse. |
These did not move: date, location, timezone, ayanamsa, specialYogas, anandadiYoga, festivals, eclipse, chandraBalam, tarabala.
For getInstantPanchang: tithi / nakshatra / yoga / karana / vara → angas.*; siderealSun → sun.siderealLongitude; siderealMoon → moon.siderealLongitude; suryaNakshatra → sun.nakshatra; chandraRashi → moon.rashi; chandramasa / samvat → calendar.*; panchaka / gandaMula → inauspicious.*.
Lahiri ayanamsa corrected by +38″
The old Lahiri constant sat 38 arcseconds behind the reference almanac's. The replacement was solved from the almanac's own published values across 1950–2050, which agree on it to within 0.01″. Every sidereal output moves with it:
| Output | Effect |
|---|---|
| Nakshatra end-times | ~69 s later than 4.x (carries the ayanamsa once) |
| Yoga end-times | ~129 s later than 4.x (carries it twice) |
| Planetary longitudes, rashi, pada, lagna, divisionals, dashas | shifted +0.0106° |
| Tithi / karana end-times | unchanged — Moon − Sun cancels the ayanamsa |
| Raman / KP / True Chitra / Thirukanitham | moved by the same +38″; their offsets from Lahiri are preserved |
Worst-case end-time drift against the reference almanac dropped from 131 s to 63 s. If you have snapshot tests or cached charts from 4.x, expect to re-pin them.
ΔT comes from measurement, so every published time moves ~6 s
4.x took ΔT (TT − UT) entirely from Espenak–Meeus, whose post-2005 branches are a 2006 extrapolation. By 2026 that reads about 5.9 s high. v5 takes ΔT from the leap-second chain for the years where ΔT has actually been measured, then resumes Espenak–Meeus beyond that, carrying the accrued offset. Against JPL Horizons the measured era now agrees to 0.005 s at every decade from 1980.
| Output | Effect |
|---|---|
| Tithi / nakshatra / yoga / karana end-times | ~5.7 s later for 2025 dates |
| Sankranti and other transit instants | same shift — one uniform correction, not per-element |
| Sunrise / sunset / moonrise / moonset | barely moved |
| Dates a panchang element is filed under | unchanged except where a transit sits within seconds of sunrise |
Instant-mode vara was wrong after ~19:00
4.x getInstantPanchang looked for sunrise by searching forward from date − 12 h. For an evening instant that found tomorrow's sunrise and rolled the weekday back a day. Any query after roughly 7 pm returned the previous vara, and with it the wrong Rahu Kalam, Gulika Kalam, Yamaganda, Choghadiya, Hora and special yogas. getDailyPanchang was never affected. If you cached instant-mode results from 4.x for evening timestamps, throw them away.
precision removed
precision: 'standard' | 'high' and the Precision type are gone. Element transitions are now solved by secant iteration, which converges on the root, so the option has nothing left to select. Delete it from your options object. Leaving it in is a type error, not a silent no-op.
Sunrise, moonrise and moonset have one value per day
An event now has a single timestamp for a location and a day, whichever call asks for it. In 4.x you could get a different sunrise depending on which instant the caller searched from. Sunrise values shift by up to 108 ms against 4.x, and published moon.rise / moon.set by up to 182 ms.
read* for tables, compute* for the engine
4.x had getFestivalsForYear, which reads a pre-built table, sitting next to getFestivalsInRange, which runs the engine. The names looked alike and did different things. v5 uses one convention across all four families:
| 4.x | v5 | What it does |
|---|---|---|
getFestivalsForYear / ForDate / YearRange | readFestivalsForYear / … | reads a table |
getEclipsesForYear / ForDate / YearRange | readEclipsesForYear / … | reads a table |
getMoonPhasesForYear / ForDate / YearRange | readMoonPhasesForYear / … | reads a table |
| — | readMuhurtaForYear / ForDate / YearRange / readBestMuhurtaDays | new — reads a table |
getFestivalsInRange | computeFestivalsInRange | runs the engine |
getEclipsesInRange | computeEclipsesInRange | runs the engine |
getMoonPhasesInRange | computeMoonPhasesInRange | runs the engine |
findAuspiciousDates | computeAuspiciousDatesInRange | runs the engine |
getEkadashiDatesForYear | computeEkadashiDatesForYear | runs the engine |
getSankrantisForYear | computeSankrantisForYear | runs the engine |
New single-year entry points, the shape most callers reach for first:
computeFestivalsForYear(2027, location, { timezone: 330 });
computeEclipsesForYear(2027, location, { timezone: 330 });
computeMoonPhasesForYear(2027, { timezone: 330 });
computeAuspiciousDatesForYear(2027, vivahRule, location, { timezone: 330 });result.timezone is now an object
- result.timezone // 330
+ result.timezone // { offsetMinutes: 330, zone: 'Asia/Kolkata' }
+ result.timezone.offsetMinuteszone is present only when you passed a zone name.
The DST limit: the offset is resolved once per call from a reference date, so a Hindu day that contains a DST transition is computed at one offset throughout. That is correct on almost every day. On the one or two transition days a year, times after the jump are shifted by the size of the jump.
One rule for “not applicable”: always present, null or []
- if ('chandraBalam' in r) … // 4.x: field absent without janmaRashi
- r.chandraBalam!.house // and the `!` was mandatory
+ if (r.chandraBalam !== null) … // v5: always present, null when unasked
+ r.chandraBalam?.houseOnly chandraBalam and tarabala changed behaviour. Everything else already followed the rule. toBeUndefined()-style test assertions against them become toBeNull().
Smaller changes
suryaNakshatratyped correctly. It is nowsun.nakshatra. Its index has always been 0..26, but its 4.x type saidRashiInfo(0..11). The runtime value is unchanged. The type is nowNakshatraIndexInfo, and TypeScript will point at any misuse._debugremoved. It was declared in the published type and never written, so it was alwaysundefined.- Alias fields are documented rather than removed.
sun.dinamanaMinutes/sun.dayDurationMinutes(and the night pair) are the same numbers under classical and English names, and the types now say so.calendar.masais documented as the solar month andcalendar.chandramasaas the lunar one. - Built tables are smaller. A 10-year festival table is roughly a quarter the size of the 4.x layout, about 97 KB bilingual for one city. It also parses faster and holds less memory on device. Tables you already cached still read, because the reader detects the format.
New in v5
EclipseInfo/EclipseSubtypeare now exported.festivals[].key— a stable, language-independent festival id. Match on this, never onname.bhadra.locationName— a localized display name beside the machine-readablebhadra.location.MuhurtaScore.factors— structured scoring inputs alongside the Englishreasons.BirthChart.byPlanet— the nine placements keyed by graha.eclipse.descriptionis now localized underlanguage: 'hi'.sectionsongetDailyPanchang— ask for a narrower, cheaper call. See Performance.buildMuhurtaTableand thepanchang-ts/muhurtasubpath. See Muhurta Engine.
