- Dharmagya
- panchang-ts docs
- Upgrading from 4.x
Upgrading from 4.x
panchang-ts · v5.0.0 · MIT
Every breaking change in v5: real Date instants, the grouped result, 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. The rest is renames — mechanical, and listed in full below.
Published Dates are real instants — the flagship change
- 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"Through 4.x every published Date was the true instant shifted by the UTC offset, and the README told you to read it back with getUTC*. That worked only as long as you did nothing else with the value: JSON.stringify emitted a wrong instant labelled Z, Intl with a timeZone rendered 12:39 pm for an 07:09 am sunrise, and any comparison, diff, database write, date-fns or Temporal call was off by the offset.
v5 applies the fix to 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 sutak window. Each gains a *Local companion 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.
Cost: rendering the strings adds ~0.04 ms per daily panchang — invisible on a cold call and ~20% of a fully cached warm one. The release still warms to 0.17 ms, against published 4.3.1's 6.20 ms.
The result object is grouped
DailyPanchangResult had ~50 flat top-level fields mixing five categories. v5 sorts them into seven groups — see the group table. This is a large break, and it lands in the same release as the Date change on purpose: migrating both at once is one pass over your read sites, not 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 — same value; the new eclipse.magnitude is the diameter fraction catalogues publish, negative for a penumbral lunar eclipse |
Unmoved: 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 library's Lahiri constant sat 38 arcseconds behind DrikPanchang's. The replacement was solved from Drik's own published values across 1950–2050, which agree on it to within 0.01″ — a century-wide baseline, so the precession polynomial is pinned too. 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 vs Drik dropped from 131 s to 60 s. If you have snapshot tests or cached charts from 4.x, expect them to need re-pinning.
ΔT now uses 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 it reads about 5.9 s high. v5 takes ΔT from the leap-second chain wherever ΔT has actually been measured, and resumes Espenak–Meeus beyond it 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 located sunrise by searching forward from date − 12 h; for an evening instant it 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, discard them.
precision removed
precision: 'standard' | 'high' and the Precision type no longer exist. Element transitions are now solved by secant iteration, which converges to the root, so there is nothing left for the option to select. Removing it from your options object is the whole migration; leaving it in is a type error, not a silent no-op.
Sunrise, moonrise and moonset are single-valued per location-day
Solar and lunar rise/set events resolve through canonical per-location-day caches, so an event has one timestamp no matter which caller asks. Sunrise values shift by ≤108 ms vs 4.x; published moon.rise / moon.set by ≤182 ms. This removes an inconsistency: 4.x returned a different sunrise depending on which instant the caller happened to search from.
read* for tables, compute* for the engine
4.x had getFestivalsForYear (reads a pre-built table) beside getFestivalsInRange (runs the engine) — near-identical names, completely different semantics. v5 settles 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, stated explicitly: the offset is resolved once per call from a reference date, so a Hindu day containing a DST transition is computed at a single offset throughout. Correct for almost every day; on the one or two transition days a year, times after the jump are shifted by its size.
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. 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 nowNakshatraIndexInfoand TypeScript will point at any misuse._debugremoved. It was declared in the published type and written nowhere — alwaysundefined.- Alias fields documented rather than removed.
sun.dinamanaMinutes/sun.dayDurationMinutes(and the night pair) are the same numbers under classical and English names; the types now say so.calendar.masais documented as the solar month andcalendar.chandramasathe lunar one. - Built tables are dictionary-encoded. A 10-year festival table drops from 315 KB to 89.9 KB; the win is parse time and resident memory on Hermes. Tables you already cached still read — the reader detects the format.
Additive, but worth knowing
EclipseInfo/EclipseSubtypeare now exported.festivals[].key— stable, language-independent festival id. Match on this, never onname.bhadra.locationName— localized display name beside the machine-readablebhadra.location.MuhurtaScore.factors— structured scoring inputs alongside Englishreasons.BirthChart.byPlanet— the nine placements keyed by graha.eclipse.descriptionis now localized underlanguage: 'hi'.sectionsongetDailyPanchang— opt into a narrower, cheaper call. See Performance.buildMuhurtaTable+ thepanchang-ts/muhurtasubpath — see Muhurta Engine.
