panchang-ts 5.1.1 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +225 -107
  2. package/dist/calendar/eclipsesTable.d.cts +5 -36
  3. package/dist/calendar/eclipsesTable.d.ts +5 -36
  4. package/dist/calendar/festivalsTable.cjs +0 -2
  5. package/dist/calendar/festivalsTable.d.cts +5 -35
  6. package/dist/calendar/festivalsTable.d.ts +5 -35
  7. package/dist/calendar/festivalsTable.js +0 -2
  8. package/dist/calendar/moonPhasesTable.cjs +0 -2
  9. package/dist/calendar/moonPhasesTable.d.cts +5 -35
  10. package/dist/calendar/moonPhasesTable.d.ts +5 -35
  11. package/dist/calendar/moonPhasesTable.js +0 -2
  12. package/dist/eclipsesTableTypes-NXQJdqX5.d.cts +72 -0
  13. package/dist/eclipsesTableTypes-NXQJdqX5.d.ts +72 -0
  14. package/dist/{festivalsTableTypes-CzDgY4zS.d.cts → festivalsTableTypes-DrGZtRjB.d.cts} +2 -39
  15. package/dist/{festivalsTableTypes-CzDgY4zS.d.ts → festivalsTableTypes-DrGZtRjB.d.ts} +2 -39
  16. package/dist/index.cjs +411 -645
  17. package/dist/index.d.cts +402 -4627
  18. package/dist/index.d.ts +402 -4627
  19. package/dist/index.js +406 -646
  20. package/dist/moonPhasesTableTypes-DQcbizmB.d.cts +64 -0
  21. package/dist/moonPhasesTableTypes-DQcbizmB.d.ts +64 -0
  22. package/dist/muhurta/muhurtaTable.cjs +0 -2
  23. package/dist/muhurta/muhurtaTable.d.cts +5 -36
  24. package/dist/muhurta/muhurtaTable.d.ts +5 -36
  25. package/dist/muhurta/muhurtaTable.js +0 -2
  26. package/dist/muhurtaTableTypes-D8iSFzSI.d.cts +52 -0
  27. package/dist/muhurtaTableTypes-D8iSFzSI.d.ts +52 -0
  28. package/package.json +10 -9
  29. package/dist/eclipsesTableTypes-BFYWRA44.d.cts +0 -108
  30. package/dist/eclipsesTableTypes-BFYWRA44.d.ts +0 -108
  31. package/dist/moonPhasesTableTypes-9XLibNCZ.d.cts +0 -103
  32. package/dist/moonPhasesTableTypes-9XLibNCZ.d.ts +0 -103
  33. package/dist/muhurtaTableTypes-CgBom297.d.cts +0 -81
  34. package/dist/muhurtaTableTypes-CgBom297.d.ts +0 -81
package/README.md CHANGED
@@ -6,12 +6,16 @@
6
6
  Pure TypeScript Hindu Panchang (almanac), Jyotish, and Birth Chart calculations.
7
7
  Zero runtime dependencies. Works offline in React Native (Hermes), Node.js, and browsers.
8
8
 
9
- **Fast** (~0.25 ms trimmed, ~0.41 ms full) · **Typed** (full TypeScript) · **Offline** (pure JS math) · **8,772 tests across 137 files**
9
+ **Fast** (~0.25 ms trimmed, ~0.41 ms full) · **Typed** (full TypeScript) · **Offline** (pure JS math) · **8,805 tests across 138 files**
10
10
 
11
11
  > 📖 **Full documentation: [dharmagya.app/docs/panchang-ts](https://dharmagya.app/docs/panchang-ts)**
12
- > This README covers install, quick start, and the 5.0 → 5.1 and 4.x → 5 migrations in full,
13
- > plus a per-feature quick reference. The complete reference every option, result field,
14
- > table format, accuracy bound and performance note lives on the docs site.
12
+ > This README covers install, quick start, and the 5.1 → 5.2, 5.0 → 5.1 and 4.x → 5 migrations
13
+ > in full, plus a per-feature quick reference. The complete reference (every option, result field,
14
+ > table format, accuracy bound and performance note) lives on the docs site.
15
+
16
+ A Go port of the same engine ships from the same repository, held to this package's
17
+ exact arithmetic by a parity harness. See
18
+ [the repository](https://github.com/ishankgupta95/panchang-ts) if you need it.
15
19
 
16
20
  ---
17
21
 
@@ -46,12 +50,12 @@ console.log(result!.calendar.samvat.vikramSamvat); // 2081
46
50
 
47
51
  ### Reading Output Times
48
52
 
49
- Every `Date` in a result is a **real instant** `.getTime()` is the correct
53
+ Every `Date` in a result is a **real instant**: `.getTime()` is the correct
50
54
  epoch millisecond. Every instant has a `*Local` companion: an offset-carrying
51
55
  ISO 8601 string, which is what you want for display.
52
56
 
53
57
  ```typescript
54
- result!.sun.rise; // Date 2025-01-14T01:39:44.172Z (the actual moment)
58
+ result!.sun.rise; // Date: 2025-01-14T01:39:44.172Z (the actual moment)
55
59
  result!.sun.riseLocal; // "2025-01-14T07:09:44.172+05:30"
56
60
  result!.inauspicious.rahuKalam.start; // Date
57
61
  result!.inauspicious.rahuKalam.startLocal;
@@ -73,7 +77,7 @@ const noon = new Date((result!.sun.rise.getTime() + result!.sun.set.getTime()) /
73
77
  formatInZone(noon, result!.timezone.offsetMinutes); // "2025-01-14T12:27:31.086+05:30"
74
78
  ```
75
79
 
76
- > **Changed in v5 this is the breaking change most likely to affect you.**
80
+ > **Changed in v5. This is the breaking change most likely to affect you.**
77
81
  > Through 4.x every published `Date` was the true instant *shifted* by the UTC
78
82
  > offset, and the README told you to read it back with `getUTC*`. That worked
79
83
  > only as long as you did nothing else with the value: `JSON.stringify` emitted
@@ -84,7 +88,7 @@ formatInZone(noon, result!.timezone.offsetMinutes); // "2025-01-14T12:27:31.086
84
88
  > Migration is mechanical: `x.getUTCHours()` → read `xLocal`, or format the
85
89
  > instant. See [Upgrading from 4.x](#upgrading-from-4x).
86
90
 
87
- `moon.rise` / `moon.set` can be `null` the Moon occasionally does not rise or
91
+ `moon.rise` / `moon.set` can be `null`: the Moon occasionally does not rise or
88
92
  set on a given calendar day, which is normal. `moon.riseLocal` / `moon.setLocal`
89
93
  are `null` exactly when they are.
90
94
 
@@ -97,7 +101,7 @@ Through 4.x it was ~50 flat fields; the groups are what tell you where to look.
97
101
  |---|---|
98
102
  | `sun` | `rise` / `set` / `nextRise` (+ `*Local`), day and night lengths, the Sun's `siderealLongitude` and `nakshatra` |
99
103
  | `moon` | `rise` / `set` (+ `*Local`), the Moon's `siderealLongitude` and `rashi` |
100
- | `angas` | the five limbs `tithis`, `nakshatras`, `yogas`, `karanas`, `vara` |
104
+ | `angas` | the five limbs: `tithis`, `nakshatras`, `yogas`, `karanas`, `vara` |
101
105
  | `calendar` | `masa` (solar), `chandramasa` (lunar), `samvat` |
102
106
  | `muhurtas` | `abhijit`, `brahma`, `vijaya`, `godhuli`, `nishita`, `amritKala`, `madhyahna`, `pratahSandhya`, `sayahnaSandhya`, `doGhati` |
103
107
  | `inauspicious` | `rahuKalam`, `gulikaKalam`, `yamaganda`, `durMuhurta`, `varjyam`, `bhadra`, `gandaMula`, `panchaka`, `panchakaInfo`, `panchakaRahita` |
@@ -109,7 +113,7 @@ Top level: `date`, `location`, `timezone`, `ayanamsa`, `specialYogas`,
109
113
  **Nothing is optional.** Every field is always present. A value that does not
110
114
  apply is `null`; a collection that does not apply is `[]`. That holds whether
111
115
  the reason is the domain (no Bhadra window today) or your options (you did not
112
- pass `janmaRashi`, so `chandraBalam` is `null`) the result *shape* never
116
+ pass `janmaRashi`, so `chandraBalam` is `null`). The result *shape* never
113
117
  depends on what you passed.
114
118
 
115
119
  `getInstantPanchang` uses the same group names for the subset an instant can
@@ -124,32 +128,148 @@ answer: `sun`, `moon`, `angas`, `calendar`, `inauspicious`. There is no
124
128
  | Single-moment snapshot ("what's active right now?") or birth-chart casting | `getInstantPanchang` |
125
129
 
126
130
  `getInstantPanchang` emits a `festivals` field but only checks rule predicates
127
- at the given instant it skips canonical-time refinements (madhyahna /
131
+ at the given instant: it skips canonical-time refinements (madhyahna /
128
132
  pradosha / nishita / chandrodaya), transit-based Sankranti, and Smarta/Vaishnava
129
133
  Ekadashi split. For reliable festival dating, use `getDailyPanchang`.
130
134
 
135
+ ### If you have no location
136
+
137
+ `location` is required and stays required. Nothing here silently guesses where you are, because a
138
+ panchang computed for the wrong place does not look wrong: it returns a complete, plausible result
139
+ with some dates off by one.
140
+
141
+ There are three reference frames, and a result is always in one of them:
142
+
143
+ | frame | point | when |
144
+ |---|---|---|
145
+ | `practical` | the location you passed | you have real coordinates. The only frame correct for a real user |
146
+ | `traditional` | **Ujjain** 23.1765N, 75.7885E | the classical madhya rekha of the Surya Siddhanta. The default |
147
+ | `modern` | **Central Station** 23.1833N, 82.5E | the 1955 Calendar Reform Committee reference for the Rashtriya Panchang |
148
+
149
+ Why the frame matters at all: a tithi, nakshatra, yoga or karana **ends at one instant worldwide**.
150
+ Only the civil day it gets labelled with depends on your sunrise, along with everything else
151
+ rise/set derived (rahu kalam, choghadiya, pradosha, nishita, moonrise anchors).
152
+
153
+ ```typescript
154
+ import { getDailyPanchang, resolveLocation, IST_OFFSET_MINUTES } from 'panchang-ts';
155
+
156
+ // maybeLocation may be undefined; traditional (Ujjain) is the default frame
157
+ const { location, reference } = resolveLocation(maybeLocation);
158
+ const day = getDailyPanchang(date, location, { timezone: IST_OFFSET_MINUTES });
159
+
160
+ if (reference !== 'practical') {
161
+ // say so, the way a printed panchang names the city it was computed for
162
+ console.info(`Computed for the ${reference} reference, not your location.`);
163
+ }
164
+ ```
165
+
166
+ Pass `'modern'` as the second argument to fall back to the Central Station instead, or use
167
+ `referenceLocation('traditional' | 'modern')` if you only want the point. `'practical'` is
168
+ reported, never selected: passing a location is what makes a result practical.
169
+
170
+ **Price the fallbacks before you rely on them.** Measured over 730 days against 48 Indian cities,
171
+ the tithi at the reference's sunrise differs from the city's own on up to **5.9% of days** for
172
+ Ujjain and **4.0%** for the Central Station. Ujjain is the better of the two for most users
173
+ (population-weighted 1.18% against 1.80%, since India's metros lie west of the IST meridian) and
174
+ the worse in the tail. Outside India both degrade without limit.
175
+
176
+ `IST_OFFSET_MINUTES` (330) is exact for the Central Station, whose longitude is the IST meridian.
177
+ For Ujjain it is the civil clock rather than its local mean time, which is 303.2 minutes; the
178
+ 26.8-minute gap is what the classical deshantara correction exists to close.
179
+
180
+ A half-filled location throws rather than being completed from a reference point, and
181
+ `{ latitude: 0, longitude: 0 }` is treated as the real place it is, not as missing.
182
+
183
+ Note that the shipped static tables (`panchang-ts/festivals`, `/eclipses`, `/muhurta`) are
184
+ pre-computed for Varanasi, which is a fourth point again.
185
+
186
+ ---
187
+
188
+ ## Upgrading to 5.2
189
+
190
+ No type breaks and no removed exports. What changes is output: a set of dates that
191
+ were wrong, or missing entirely, are now right. If you cache festival dates, diff
192
+ them against a rebuild before you ship, and key on `festivals[].key` rather than on
193
+ the label, because 27 user-visible strings changed punctuation in both `en` and `hi`.
194
+
195
+ ### Festivals that used to vanish for a whole year now appear
196
+
197
+ A tithi that begins and ends between two consecutive sunrises is current at no
198
+ sunrise. A rule keyed on the sunrise tithi matched it on no day of the year, so the
199
+ festival disappeared from that year entirely. Ugadi and Gudi Padwa were absent from
200
+ 2026 at every location; Navaratri 2027, Gangaur 2025, both Teejes, Govardhan Puja,
201
+ Bhai Dooj, Anant Chaturdashi, Kartika Purnima and Chhath Kharna were missing at some
202
+ cities and present at others. The Hindu day that wholly contains the tithi now claims it, which
203
+ is what the reference almanac publishes.
204
+
205
+ Measured over eight Indian cities across 2020 to 2030: **87 dates added, and no
206
+ existing date moves.** Every addition belongs to a city and year where the festival
207
+ previously had no date at all, so nothing is rescheduled.
208
+
209
+ Four festivals are deliberately left out, because the reference does not put them on
210
+ the containing day: Narak Chaturdashi and Chhath Usha Arghya, which it publishes on
211
+ the day the tithi ends, and Holi and Phagli, which are anchored to pradosha.
212
+
213
+ ### Three festivals move to a different date
214
+
215
+ These are the ones to diff for. They are corrections, not additions.
216
+
217
+ | festival | was | now |
218
+ |---|---|---|
219
+ | Vat Savitri Amavasya | roughly 30 days late, every year | 7 of 7 checked years match the reference |
220
+ | Rig Upakarma | 9 of 13 checked years right | 13 of 13 |
221
+ | Sama Upakarma | 4 of 13 checked years right | 13 of 13 |
222
+
223
+ Vat Savitri Amavasya was filed under the wrong lunar month. The vrat falls on
224
+ purnimanta Jyeshtha Amavasya, and an amavasya ends its amanta month, so the two
225
+ namings sit a month apart; the rule had been selecting the following new moon since
226
+ the festival was added. It is now also anchored to aparahna rather than sunrise, with
227
+ the later day taking a span that reaches aparahna twice. `vat_savitri_purnima` is
228
+ unaffected and still correct, because a purnima sits inside the amanta month it names.
229
+
230
+ Both Upakarma rules matched on the nakshatra at sunrise and nothing else. Sama
231
+ Upakarma fired a **second** time in six of thirteen years, on the Bhadrapada Krishna
232
+ Amavasya that carries Hasta again, and was a day late in five more. Rig Upakarma
233
+ emitted two dates when its nakshatra spanned two sunrises (2019, 2029), a date in the
234
+ wrong paksha when the nakshatra slipped into Krishna (2020), and nothing at all in
235
+ the one year it fell outside Shukla paksha entirely (2022). Both now emit exactly one date in all 88
236
+ city-years of the eight-city sweep. Sama Upakarma is anchored to aparahna and is
237
+ therefore longitude sensitive: in 2026 it is the 13th of September at Kolkata and the
238
+ 12th elsewhere, which is what the reference publishes.
239
+
240
+ ### Numbers that changed
241
+
242
+ `getUpcomingSolarEclipse` did not check that an eclipse ended after the instant it
243
+ was asked to search from, so range walkers could return the same eclipse repeatedly
244
+ and drop others. `computeEclipsesInRange(2000 to 2011, Varanasi)` returned 277
245
+ entries of which 249 were one 2007 partial; it now returns 32 distinct entries.
246
+ Shadbala Kala Bala is now anchored to the preceding sunrise rather than the following
247
+ one, moving 1009 of 2016 sampled births. Narayan dasha durations exclude adjacent
248
+ signs from rasi drishti. Gulika and Mandi use a day/night test that matches their own
249
+ definition.
250
+
131
251
  ---
132
252
 
133
- ## Upgrading to 5.1 part 2
253
+ ## Upgrading to 5.1, part 2
134
254
 
135
255
  Two deliberate type breaks, both correcting fields that did not match
136
- DrikPanchang (the project's parity oracle), in the same style as part 1's
256
+ the reference almanac (the project's parity oracle), in the same style as part 1's
137
257
  `varjyam` break.
138
258
 
139
259
  ### `inauspicious.durMuhurta` is now `DurMuhurtaPeriod[]`
140
260
 
141
261
  ```diff
142
262
  - const [dm1, dm2] = r.inauspicious.durMuhurta;
143
- + r.inauspicious.durMuhurta.forEach((dm) => show(dm)); // 12 windows
263
+ + r.inauspicious.durMuhurta.forEach((dm) => show(dm)); // 1-2 windows
144
264
  + // dm.segment is 'day' or 'night' (only Tuesday carries a night window)
145
265
  ```
146
266
 
147
- The old table emitted two day windows every day and matched drik on none of
267
+ The old table emitted two day windows every day and matched the almanac on none of
148
268
  the seven weekdays. The corrected classical Muhurta-Chintamani table gives
149
- one window on Sunday and Wednesday, two elsewhere and Tuesday's second
269
+ one window on Sunday and Wednesday, two elsewhere, and Tuesday's second
150
270
  window falls at **night** (the 7th of the 15 sunset→sunrise muhurtas), so
151
271
  the exactly-two-day-windows tuple could not survive. Verified against 58
152
- consecutive drik day-pages across two cities; a full drik week is pinned.
272
+ consecutive almanac day-pages across two cities; a full almanac week is pinned.
153
273
 
154
274
  ### `muhurtas.amritKala` is now `TimePeriod[]`
155
275
 
@@ -158,18 +278,18 @@ consecutive drik day-pages across two cities; a full drik week is pinned.
158
278
  + r.muhurtas.amritKala.forEach(show); // [] when the day has none
159
279
  ```
160
280
 
161
- Amrit Kala shares Varjyam's architecture (drik prints them from the same
281
+ Amrit Kala shares Varjyam's architecture (the almanac prints them from the same
162
282
  frame): each window anchors at its nakshatra's own start, offset by a
163
283
  per-nakshatra count of nakshatra-elastic ghatikas, spans exactly 4 such
164
- ghatikas, and belongs to the Hindu day its **start** falls in 02 windows
165
- per day. The old sunrise-anchored single window disagreed with drik by up
284
+ ghatikas, and belongs to the Hindu day its **start** falls in (0-2 windows
285
+ per day). The old sunrise-anchored single window disagreed with the almanac by up
166
286
  to ~16 h. `computeAmritKala` is replaced by
167
287
  `computeAmritKalaWindows(sunriseUtc, nextSunriseUtc, getMoon)`.
168
288
 
169
289
  ### Ekadashi splits: Smarta first, Vaishnava second
170
290
 
171
- On the days drik prints an Ekadashi twice, the **earlier** day is the Smarta
172
- fast and the **later** one the Vaishnava fast drik says so in prose on every
291
+ On the days the almanac prints an Ekadashi twice, the **earlier** day is the Smarta
292
+ fast and the **later** one the Vaishnava fast. The almanac says so in prose on every
173
293
  Ekadashi date-time page. Two corrections bring the library in line:
174
294
 
175
295
  ```diff
@@ -183,11 +303,10 @@ Ekadashi date-time page. Two corrections bring the library in line:
183
303
  ```
184
304
 
185
305
  Both the Dashami-viddha and the vriddha-Dwadashi (Pakshavardhini) splits now
186
- match drik across every pair it publishes in 20242028. `getDailyPanchang`
306
+ match the almanac across every pair it publishes in 2024-2028. `getDailyPanchang`
187
307
  callers that keyed off `smarta_ekadashi` / `vaishnava_ekadashi` on split days
188
308
  will see the two swap places; `computeEkadashiDatesForYear` is unchanged.
189
- Custom locale packs need the renamed viddha description keys — see the
190
- CHANGELOG.
309
+ Custom locale packs need the renamed viddha description keys.
191
310
 
192
311
  ### Regional solar new years land on their own days
193
312
 
@@ -203,12 +322,11 @@ keyed, with a piecewise `vasa` segment list on `BhadraInfo`), night-transit
203
322
  Sankranti dates (+ a new `moment` field on `SankrantiEvent`), kshaya-Dwadashi
204
323
  Ekadashi advance, Vijayadashami/Karva Chauth/Janmashtami kala rules, the
205
324
  Kali Yuga year boundary, `scoreMuhurta` special-yoga parity, eastern-
206
- longitude Gulika/Mandi, and the opt-in Ashtakavarga reductions. See the
207
- CHANGELOG for each rule and its drik evidence.
325
+ longitude Gulika/Mandi, and the opt-in Ashtakavarga reductions.
208
326
 
209
327
  ---
210
328
 
211
- ## Upgrading to 5.1 part 1 (from 5.0)
329
+ ## Upgrading to 5.1, part 1 (from 5.0)
212
330
 
213
331
  One deliberate type break, two corrected dasha tables, and one opt-in flag.
214
332
 
@@ -221,24 +339,24 @@ One deliberate type break, two corrected dasha tables, and one opt-in flag.
221
339
 
222
340
  5.0 evaluated only the nakshatra active at sunrise and dropped the second
223
341
  Varjyam window printed panchangs show on transition days. 5.1 publishes every
224
- window, in start order, under DrikPanchang's attribution rule: a window belongs
342
+ window, in start order, under the almanac's attribution rule: a window belongs
225
343
  to the Hindu day its **start** falls in (one that begins before sunrise and
226
- runs past it is yesterday's), and window instants are unclamped an end can
344
+ runs past it is yesterday's), and window instants are unclamped, so an end can
227
345
  land after next sunrise. Validated window-for-window against a 61-day
228
- DrikPanchang sweep (AugSep 2026, two full nakshatra cycles): 62/62 match.
346
+ reference-almanac sweep (Aug-Sep 2026, two full nakshatra cycles): 62/62 match.
229
347
 
230
348
  Two value-level corrections ride along: **Mula carries a second tyajya spell**
231
- (elapsed ghatikas 20 *and* 56 DrikPanchang, ProKerala and B.V. Raman's
349
+ (elapsed ghatikas 20 *and* 56; the reference almanac, ProKerala and B.V. Raman's
232
350
  *Muhurta* concur), so Mula days now emit the window 5.0 missed; and the
233
351
  standalone `computeVarjyam` primitive returns the *earliest* of a nakshatra's
234
352
  spells overlapping the day. New export: `computeVarjyamWindows(sunriseUtc,
235
353
  nextSunriseUtc, getMoon)`.
236
354
 
237
- ### Yogini and Ashtottari starting lords were wrong now classical
355
+ ### Yogini and Ashtottari starting lords were wrong, now classical
238
356
 
239
357
  - **Yogini** used `nakshatraIndex % 8`, off by three Yoginis for every birth.
240
- Now the classical Devi-Bhagavata formula (1-based janma nakshatra + 3) mod
241
- 8; remainder 1 = Mangala … 0 = Sankata so Ashwini → Bhramari, Pushya →
358
+ Now the classical Devi-Bhagavata formula: (1-based janma nakshatra + 3) mod
359
+ 8, remainder 1 = Mangala … 0 = Sankata, so Ashwini → Bhramari, Pushya →
242
360
  Dhanya. Verified against published worked examples and PyJHora.
243
361
  - **Ashtottari** used a years-proportional split of the zodiac from a Krittika
244
362
  anchor, matching no source. Now the classical Ardradi **group table**
@@ -256,11 +374,11 @@ and should be discarded.
256
374
  computeAshtakoot(boy, girl, { ganaCancellation: true });
257
375
  ```
258
376
 
259
- Default output is byte-identical to 5.0 (DrikPanchang's published 36-guna
260
- table applies no Gana cancellation, and drik parity stays the default
377
+ Default output is byte-identical to 5.0 (the reference almanac's published 36-guna
378
+ table applies no Gana cancellation, and reference-almanac parity stays the default
261
379
  standard). With the flag raised, a doshic Gana score (≤ 1) is restored to the
262
380
  full 6 when the two Moons' sign lords are the same graha or mutual naisargika
263
- friends, recorded in `cancellations` the condition set attested across
381
+ friends, recorded in `cancellations`: the condition set attested across
264
382
  independent pandit corpora; weaker ones are documented on `AshtakootOptions`
265
383
  and deliberately not encoded.
266
384
 
@@ -272,10 +390,10 @@ Three changes move numbers that 4.x produced, and one option is gone.
272
390
 
273
391
  ### Lahiri ayanamsa corrected by +38″
274
392
 
275
- The library's Lahiri constant sat 38 arcseconds behind DrikPanchang's it used
276
- `23.853211°` at J2000 (the widely-repeated 23° 51′ 11.6″ figure) where Drik
277
- computes `23.863801°`. The replacement was solved from Drik's own published
278
- values across 19502050, which agree on it to within 0.01 a century-wide
393
+ The library's Lahiri constant sat 38 arcseconds behind the reference almanac's: it
394
+ used `23.853211°` at J2000 (the widely-repeated 23° 51′ 11.6″ figure) where the
395
+ almanac computes `23.863801°`. The replacement was solved from the almanac's own published
396
+ values across 1950-2050, which agree on it to within 0.01″, a century-wide
279
397
  baseline, so the precession polynomial is pinned too, not just the epoch
280
398
  constant. Every sidereal output moves with it:
281
399
 
@@ -284,32 +402,32 @@ constant. Every sidereal output moves with it:
284
402
  | Nakshatra end-times | ~69 s later than 4.x (carries the ayanamsa once) |
285
403
  | Yoga end-times | ~129 s later than 4.x (carries it twice) |
286
404
  | Planetary longitudes, rashi, pada, lagna, divisionals, dashas | shifted +0.0106° |
287
- | Tithi / karana end-times | unchanged Moon − Sun cancels the ayanamsa |
405
+ | Tithi / karana end-times | unchanged: Moon − Sun cancels the ayanamsa |
288
406
  | Raman / KP / True Chitra / Thirukanitham | moved by the same +38″; their offsets from Lahiri are preserved |
289
407
 
290
- Worst-case end-time drift vs Drik dropped from 131 s to 60 s, and the sign split
291
- by ayanamsa exposure nakshatra and yoga early, tithi and karana late is gone.
408
+ Worst-case end-time drift vs the almanac dropped from 131 s to 60 s, and the sign split
409
+ by ayanamsa exposure (nakshatra and yoga early, tithi and karana late) is gone.
292
410
  If you have snapshot tests or cached charts from 4.x, expect them to need
293
411
  re-pinning.
294
412
 
295
413
  ### ΔT now uses measurement, so every published time moves ~6 s
296
414
 
297
- 4.x took ΔT (TT − UT) entirely from EspenakMeeus. Its post-2005 branches are an
298
- extrapolation published in 2006, and Earth's rotation did not follow it — by
415
+ 4.x took ΔT (TT − UT) entirely from Espenak-Meeus. Its post-2005 branches are an
416
+ extrapolation published in 2006, and Earth's rotation did not follow it. By
299
417
  2026 the model reads about **5.9 s high**, drifting a further ~0.6 s each year.
300
418
  Because this library reports *times*, that lands directly on published values.
301
419
 
302
420
  v5 takes ΔT from the leap-second chain (`32.184 + (TAI − UTC)`, exact, and
303
421
  within the 0.9 s band leap seconds maintain) wherever ΔT has actually been
304
- measured, and resumes EspenakMeeus beyond it carrying the offset it had
422
+ measured, and resumes Espenak-Meeus beyond it carrying the offset it had
305
423
  accrued. Against JPL Horizons the measured era now agrees to **0.005 s** at
306
424
  every decade from 1980, where 4.x was seconds out.
307
425
 
308
426
  | Output | Effect |
309
427
  |---|---|
310
428
  | Tithi / nakshatra / yoga / karana end-times | ~5.7 s later for 2025 dates, growing with the model's drift |
311
- | Sankranti and other transit instants | same shift it is one uniform correction, not per-element |
312
- | Sunrise / sunset / moonrise / moonset | barely moved the error scales against the 15°/hr sky rotation |
429
+ | Sankranti and other transit instants | same shift: it is one uniform correction, not per-element |
430
+ | Sunrise / sunset / moonrise / moonset | barely moved: the error scales against the 15°/hr sky rotation |
313
431
  | Dates a panchang element is *filed under* | unchanged except where a transit sits within seconds of sunrise |
314
432
 
315
433
  That last row is the one to know about. `computeSankrantisForYear` publishes a
@@ -323,7 +441,7 @@ near sunrise are where a day could flip.
323
441
  `getInstantPanchang` located sunrise by searching forward from `date − 12 h`.
324
442
  For an evening instant that start point is already past the morning's sunrise,
325
443
  so it found *tomorrow's* and rolled the weekday back a day. Any query after
326
- roughly 7 pm returned the previous vara and with it the wrong Rahu Kalam,
444
+ roughly 7 pm returned the previous vara, and with it the wrong Rahu Kalam,
327
445
  Gulika Kalam, Yamaganda, Choghadiya, Hora, Anandadi yoga and special yogas.
328
446
  `getDailyPanchang` was never affected. If you cached instant-mode results from
329
447
  4.x for evening timestamps, discard them.
@@ -333,7 +451,7 @@ Gulika Kalam, Yamaganda, Choghadiya, Hora, Anandadi yoga and special yogas.
333
451
  `precision: 'standard' | 'high'` and the `Precision` type no longer exist.
334
452
  Element transitions are now solved by secant iteration, which converges to the
335
453
  root rather than stopping at a fixed tolerance, so there is nothing left for the
336
- option to select and the tighter setting no longer buys anything. Removing it
454
+ option to select, and the tighter setting no longer buys anything. Removing it
337
455
  from your options object is the whole migration; leaving it in is a type error,
338
456
  not a silent no-op.
339
457
 
@@ -343,7 +461,7 @@ Solar rise/set is computed from a canonical anchor and cached per location-day,
343
461
  so it no longer depends on which instant the caller happened to start searching
344
462
  from. Values shift by ≤108 ms vs 4.x, and two calls for the same day now agree
345
463
  exactly instead of differing by up to 109 ms. Windows derived proportionally
346
- from the day length Varjyam, Bhadra, the slot systems move by a little more
464
+ from the day length (Varjyam, Bhadra, the slot systems) move by a little more
347
465
  than that. This removes an inconsistency rather than introducing an
348
466
  approximation: 4.x returned a different sunrise depending on which caller asked.
349
467
 
@@ -353,7 +471,7 @@ The same treatment sunrise received, now applied to the Moon. `getMoonrise` /
353
471
  `getMoonset` resolve through a canonical per-UTC-day cache, so an event has one
354
472
  timestamp no matter which caller asks or from which instant they searched.
355
473
  Published `moon.rise` / `moon.set` shift by **≤182 ms** vs 4.x. Nothing else in the
356
- result moves verified over 11,520 daily results across six locations and three
474
+ result moves, verified over 11,520 daily results across six locations and three
357
475
  centuries: zero changes to any index, name, boolean, festival date or other
358
476
  timestamp.
359
477
 
@@ -361,8 +479,8 @@ timestamp.
361
479
 
362
480
  `getFestivalsForYear` read a pre-built table; `getFestivalsInRange` ran the
363
481
  engine. Two near-identical names, completely different inputs and semantics. v5
364
- settles one convention across all four families festivals, eclipses, moon
365
- phases and muhurta:
482
+ settles one convention across all four families (festivals, eclipses, moon
483
+ phases and muhurta):
366
484
 
367
485
  | 4.x | v5 | what it does |
368
486
  |---|---|---|
@@ -371,8 +489,8 @@ phases and muhurta:
371
489
  | `getFestivalsYearRange` | `readFestivalsYearRange` | reads a table |
372
490
  | `getEclipsesForYear` / `ForDate` / `YearRange` | `readEclipsesForYear` / … | reads a table |
373
491
  | `getMoonPhasesForYear` / `ForDate` / `YearRange` | `readMoonPhasesForYear` / … | reads a table |
374
- | | `readMuhurtaForYear` / `ForDate` / `YearRange` | **new** reads a table |
375
- | | `readBestMuhurtaDays` | **new** top-scoring days |
492
+ | n/a | `readMuhurtaForYear` / `ForDate` / `YearRange` | **new**: reads a table |
493
+ | n/a | `readBestMuhurtaDays` | **new**: top-scoring days |
376
494
  | `getFestivalsInRange` | `computeFestivalsInRange` | runs the engine |
377
495
  | `getEclipsesInRange` | `computeEclipsesInRange` | runs the engine |
378
496
  | `getMoonPhasesInRange` | `computeMoonPhasesInRange` | runs the engine |
@@ -380,7 +498,7 @@ phases and muhurta:
380
498
  | `getEkadashiDatesForYear` | `computeEkadashiDatesForYear` | runs the engine |
381
499
  | `getSankrantisForYear` | `computeSankrantisForYear` | runs the engine |
382
500
 
383
- **Every 4.x name still works** they are deprecated aliases pointing at the same
501
+ **Every 4.x name still works**: they are deprecated aliases pointing at the same
384
502
  functions, kept through v5. Nothing breaks today; the old names will go in v6.
385
503
 
386
504
  New single-year entry points, the shape most callers reach for first:
@@ -419,7 +537,7 @@ Only days that pass the rule are stored unless you pass `includeFailures: true`.
419
537
  indices, rather than repeating every localized string at every occurrence. A
420
538
  10-year festival table goes from 315 KB to **89.9 KB (28.5%)**; a 10-year
421
539
  moon-phase table from 106 KB to **29.7 KB (28.0%)**. Resolved output is
422
- identical verified across every year and both locales.
540
+ identical, verified across every year and both locales.
423
541
 
424
542
  Gzip already hid most of this on the wire, so the win is **parse time and
425
543
  resident memory**, which is the constraint that actually bites on Hermes.
@@ -432,7 +550,7 @@ engine output.
432
550
  table built with 4.x keeps working; only `key` is unavailable from it, and comes
433
551
  back as `''`.
434
552
 
435
- ### Published `Date`s are real instants the flagship change
553
+ ### Published `Date`s are real instants: the flagship change
436
554
 
437
555
  ```diff
438
556
  - result.sunrise.getTime() // NOT when sunrise happened (off by the UTC offset)
@@ -441,7 +559,7 @@ back as `''`.
441
559
  + result.sun.riseLocal // "2025-01-14T07:09:44.172+05:30"
442
560
  ```
443
561
 
444
- Applies to **every** `Date` in `DailyPanchangResult` and to every `TimePeriod` —
562
+ Applies to **every** `Date` in `DailyPanchangResult` and to every `TimePeriod`:
445
563
  `sun.rise`, `sun.set`, `sun.nextRise`, `moon.rise`, `moon.set`, all the muhurtas
446
564
  and inauspicious periods, all four slot systems, the element
447
565
  `startTime`/`endTime` arrays, the eclipse contacts and sutak window. Each gains
@@ -459,7 +577,7 @@ a `*Local` companion: `sun.riseLocal`, `inauspicious.rahuKalam.startLocal`,
459
577
  `getInstantPanchang` results carry **no** `*Local` fields: that call takes no
460
578
  timezone, so there is no zone to render a wall clock in.
461
579
 
462
- **Cost:** rendering the strings adds ~0.04 ms per daily panchang invisible on
580
+ **Cost:** rendering the strings adds ~0.04 ms per daily panchang, invisible on
463
581
  a cold call and ~20% of a fully cached warm one. The release warms to
464
582
  **0.17 ms**, against published 4.3.1's **6.20 ms**.
465
583
 
@@ -472,7 +590,7 @@ a cold call and ~20% of a fully cached warm one. The release warms to
472
590
  ```
473
591
 
474
592
  `options.timezone` has always accepted `number | string`, but the result carried
475
- only a number so passing `'America/New_York'` produced a result that could not
593
+ only a number, so passing `'America/New_York'` produced a result that could not
476
594
  say which zone produced it. `zone` is present only when you passed a zone name.
477
595
 
478
596
  **The DST limit, now stated explicitly.** The offset is resolved **once per
@@ -485,7 +603,7 @@ whole truth.
485
603
  ### The result object is grouped
486
604
 
487
605
  `DailyPanchangResult` had ~50 flat top-level fields mixing five categories.
488
- v5 sorts them into seven groups see [The result is grouped](#the-result-is-grouped)
606
+ v5 sorts them into seven groups. See [The result is grouped](#the-result-is-grouped)
489
607
  for the full table. This is a large break, and it lands in the same release as
490
608
  the `Date` change on purpose: migrating both at once is one pass over your read
491
609
  sites, not two.
@@ -514,7 +632,7 @@ Every rename, in full:
514
632
  | `varjyam` / `bhadra` / `gandaMula` / `panchaka` / `panchakaRahita` | `inauspicious.*` (same names) |
515
633
  | `choghadiya` / `hora` | `periods.choghadiya` / `periods.hora` |
516
634
  | `gowriPanchangam` | `periods.gowri` |
517
- | `eclipse.magnitude` (disc *area*) | `eclipse.obscuration` same value; the new `eclipse.magnitude` is the *diameter* fraction catalogues publish, and is negative for a penumbral lunar eclipse |
635
+ | `eclipse.magnitude` (disc *area*) | `eclipse.obscuration`: same value; the new `eclipse.magnitude` is the *diameter* fraction catalogues publish, and is negative for a penumbral lunar eclipse |
518
636
 
519
637
  Unmoved: `date`, `location`, `timezone`, `ayanamsa`, `specialYogas`,
520
638
  `anandadiYoga`, `festivals`, `eclipse`, `chandraBalam`, `tarabala`.
@@ -527,12 +645,12 @@ For `getInstantPanchang`: `tithi` / `nakshatra` / `yoga` / `karana` / `vara` →
527
645
 
528
646
  ### One rule for "not applicable": always present, `null` or `[]`
529
647
 
530
- 4.x used three conventions and you could not predict which you would get
648
+ 4.x used three conventions and you could not predict which you would get:
531
649
  `| null` for `bhadra` / `varjyam` / `eclipse`, `?`-optional for `chandraBalam` /
532
650
  `tarabala`, and an empty array for `panchakaRahita` / `festivals`. v5 has one
533
651
  rule: **every field is always present**, a value that does not apply is `null`,
534
652
  and a collection that does not apply is `[]`. (Since 5.1, `varjyam` is a
535
- collection and follows the `[]` arm see [Upgrading from 5.0](#upgrading-from-50).)
653
+ collection and follows the `[]` arm; see [Upgrading from 5.0](#upgrading-from-50).)
536
654
 
537
655
  ```diff
538
656
  - if ('chandraBalam' in r) … // 4.x: field absent without janmaRashi
@@ -563,7 +681,7 @@ always `undefined`.
563
681
 
564
682
  `sun.dinamanaMinutes` / `sun.dayDurationMinutes` and `sun.ratrimanaMinutes` /
565
683
  `sun.nightDurationMinutes` are the same numbers under classical and English
566
- names. Both pairs stay consumers use both vocabularies and the types now say
684
+ names. Both pairs stay (consumers use both vocabularies) and the types now say
567
685
  plainly that they are aliases, never independently computed.
568
686
 
569
687
  `calendar.chandramasa` keeps its casing beside `moon.rashi` and `sun.nakshatra`.
@@ -575,15 +693,15 @@ the **lunar** one, which was previously left to guesswork.
575
693
 
576
694
  - `EclipseInfo` / `EclipseSubtype` are now exported. 4.x shipped
577
695
  `getUpcomingLunarEclipse` and friends without the type they return.
578
- - `festivals[].key` stable, language-independent festival id. Match on this,
696
+ - `festivals[].key`: stable, language-independent festival id. Match on this,
579
697
  never on `name`.
580
- - `bhadra.locationName` localized display name; `bhadra.location` stays the
698
+ - `bhadra.locationName`: localized display name; `bhadra.location` stays the
581
699
  machine-readable key.
582
- - `MuhurtaScore.factors` structured scoring inputs alongside English `reasons`.
583
- - `BirthChart.byPlanet` the nine placements keyed by graha.
700
+ - `MuhurtaScore.factors`: structured scoring inputs alongside English `reasons`.
701
+ - `BirthChart.byPlanet`: the nine placements keyed by graha.
584
702
  - `eclipse.description` is now localized. Under `language: 'hi'` it was
585
703
  previously emitted in English, including inside `festivals[].description`.
586
- - `sections` on `getDailyPanchang` opt into a narrower, cheaper call. See
704
+ - `sections` on `getDailyPanchang`: opt into a narrower, cheaper call. See
587
705
  [Performance](#performance).
588
706
 
589
707
  ---
@@ -592,7 +710,7 @@ the **lunar** one, which was previously left to guesswork.
592
710
 
593
711
  | Category | Features |
594
712
  |---|---|
595
- | **Pancha Anga** | Tithi, Nakshatra, Yoga, Karana, Vara with all intra-day transitions |
713
+ | **Pancha Anga** | Tithi, Nakshatra, Yoga, Karana, Vara, with all intra-day transitions |
596
714
  | **Lunar Calendar** | Chandra Masa (Purnimanta + Amanta), Adhika (leap) detection, Vikram + Shaka Samvat |
597
715
  | **Solar Calendar** | Saura Masa, Surya Nakshatra, Sankranti (transit-based) |
598
716
  | **Sun & Moon** | Sunrise, Sunset, Moonrise, Moonset (Meeus apparent-upper-limb), Chandra Rashi |
@@ -602,7 +720,7 @@ the **lunar** one, which was previously left to guesswork.
602
720
  | **Special Yogas** | Anandadi (28-cycle), Amrit Siddhi, Sarvartha Siddhi, Ravi/Guru Pushya, Dwi-/Tripushkar, Jwalamukhi, Aadal, Vidaal, Ravi |
603
721
  | **Festivals (80+)** | Ekadashi (Smarta/Vaishnava split), Pradosha, Sankranti, classical (Diwali/Holi/Shivaratri…), regional across 21 states + Nepal |
604
722
  | **Eclipses** | Solar/lunar detection, subtype, magnitude, horizon visibility, sutak window |
605
- | **Planetary Positions** | All 9 grahas (sidereal) with rashi, nakshatra, pada, retrograde mean or true Rahu/Ketu |
723
+ | **Planetary Positions** | All 9 grahas (sidereal) with rashi, nakshatra, pada, retrograde; mean or true Rahu/Ketu |
606
724
  | **Dashas** | Vimshottari (3-level), Ashtottari, Yogini, Chara, Narayan |
607
725
  | **Personal Transits** | Chandra Balam, Tarabala (9-cycle), Sade Sati |
608
726
  | **Birth Chart** | Lagna, Bhava under 3 house systems, D1/D2/D3/D7/D9/D10/D12/D30, Planetary Dignity |
@@ -620,14 +738,14 @@ the **lunar** one, which was previously left to guesswork.
620
738
 
621
739
  ## Used By
622
740
 
623
- - [dharmagya.app](https://dharmagya.app) Daily Panchang and Hindu calendar ([Play Store](https://play.google.com/store/apps/details?id=com.ishank1995.dharmagya))
741
+ - [dharmagya.app](https://dharmagya.app): Daily Panchang and Hindu calendar ([Play Store](https://play.google.com/store/apps/details?id=com.ishank1995.dharmagya))
624
742
 
625
743
  ---
626
744
 
627
745
  # Feature Reference
628
746
 
629
747
  A quick tour with runnable snippets. **Each section links to its full page on
630
- the docs site** every option, field, and caveat lives there.
748
+ the docs site**: every option, field, and caveat lives there.
631
749
 
632
750
  ## Pancha Anga & the daily result
633
751
 
@@ -639,8 +757,8 @@ const r = getDailyPanchang(date, location, { timezone: 330 })!;
639
757
  r.angas.tithis.forEach(t => console.log(t.name, t.paksha, t.endTime));
640
758
  r.angas.vara.name; // "Mangalawara"
641
759
  r.calendar.chandramasa.isAdhika; // true during leap months
642
- r.muhurtas.brahma; // TimePeriod | null and 9 more muhurtas
643
- r.inauspicious.rahuKalam; // { start, end } and 9 more windows
760
+ r.muhurtas.brahma; // TimePeriod | null, and 9 more muhurtas
761
+ r.inauspicious.rahuKalam; // { start, end }, and 9 more windows
644
762
  r.periods.choghadiya.day[0].name; // 16 Choghadiya + Gowri + 24 Hora slots
645
763
  r.anandadiYoga.name; r.specialYogas; // Anandadi + Amrit/Sarvartha Siddhi, …
646
764
 
@@ -656,7 +774,7 @@ console.log(i.angas.tithi.name, i.angas.nakshatra.name);
656
774
 
657
775
  ```typescript
658
776
  r.festivals.forEach(f => console.log(f.key, f.name, f.type, f.deferralDate));
659
- // `name` is localized, so match on `key` never on `name`:
777
+ // `name` is localized, so match on `key`, never on `name`:
660
778
  const hasDiwali = r.festivals.some(f => f.key === 'diwali');
661
779
 
662
780
  // Scope regional variants: 21 state slugs + 'nepal' + 'all' (default)
@@ -667,7 +785,7 @@ import { buildFestivalsTable } from 'panchang-ts';
667
785
  import { readFestivalsForYear, readFestivalsForDate } from 'panchang-ts/festivals';
668
786
  ```
669
787
 
670
- No table ships with the package festival dates are observer-dependent, so you
788
+ No table ships with the package: festival dates are observer-dependent, so you
671
789
  build one for your users' location and years (`npm run festivals:gen` is a
672
790
  worked example).
673
791
 
@@ -699,7 +817,7 @@ import { scoreMuhurta, computeAuspiciousDatesInRange, vivahRule } from 'panchang
699
817
 
700
818
  const s = scoreMuhurta(new Date('2026-05-12'), DELHI, vivahRule, { timezone: 330 });
701
819
  s.score; // 0..100; passes when ≥ 50
702
- s.factors; // structured, stable codes localize/filter on these
820
+ s.factors; // structured, stable codes: localize/filter on these
703
821
  s.reasons; // diagnostic English
704
822
 
705
823
  computeAuspiciousDatesInRange(vivahRule, start, end, DELHI, { timezone: 330 });
@@ -774,7 +892,7 @@ import {
774
892
 
775
893
  computeAshtakoot({ rashi: 4, nakshatra: 9 }, { rashi: 0, nakshatra: 1 });
776
894
  // → { totalScore: 0..36, koots: KootScore[8], cancellations: string[] }
777
- // Opt-in Gana-dosha cancellation (default off preserves drik 36-guna parity):
895
+ // Opt-in Gana-dosha cancellation (default off, preserves almanac 36-guna parity):
778
896
  computeAshtakoot(boy, girl, { ganaCancellation: true });
779
897
 
780
898
  computeMangalCompatibility(boyChart, girlChart); // Manglik is a PAIRWISE verdict
@@ -826,7 +944,7 @@ getHinduNewYear(2026, 'tamil-nadu', DELHI, { timezone: 330 }); // region-aware
826
944
  ```typescript
827
945
  const hi = getDailyPanchang(date, loc, { timezone: 330, language: 'hi' })!;
828
946
  hi.angas.tithis[0].name; // "कृष्ण चतुर्दशी"
829
- hi.angas.vara.englishName; // "Tuesday" englishName always English
947
+ hi.angas.vara.englishName; // "Tuesday", englishName always English
830
948
 
831
949
  // All options: timezone (number | IANA string), ayanamsa (5), language (en|hi),
832
950
  // masaSystem (purnimanta|amanta), region, computeEndTimes, sections,
@@ -834,12 +952,12 @@ hi.angas.vara.englishName; // "Tuesday" — englishName always English
834
952
  ```
835
953
 
836
954
  Machine-readable keys never change with language: match on `festival.key`,
837
- `bhadra.location`, `eclipse.kind`, `factors[].code` render `name` /
955
+ `bhadra.location`, `eclipse.kind`, `factors[].code`; render `name` /
838
956
  `locationName` / `description` / `reasons`.
839
957
 
840
958
  ## Types & Exports
841
959
 
842
- 📖 [Types & Exports →](https://dharmagya.app/docs/panchang-ts/types) the key
960
+ 📖 [Types & Exports →](https://dharmagya.app/docs/panchang-ts/types): the key
843
961
  interfaces (`TithiInfo`, `FestivalInfo`, `EclipseInfo`, `GrahaPosition`, …) and
844
962
  the complete export list of the main entry and the four engine-free subpaths
845
963
  (`panchang-ts/festivals`, `/eclipses`, `/moon-phases`, `/muhurta`).
@@ -849,7 +967,7 @@ the complete export list of the main entry and the four engine-free subpaths
849
967
  ## React Native / Hermes
850
968
 
851
969
  Works with Expo and bare React Native (Hermes engine). Pass `timezone` as a
852
- **number** IANA strings need `Intl`, which older Hermes versions lack.
970
+ **number**: IANA strings need `Intl`, which older Hermes versions lack.
853
971
 
854
972
  Two-pass rendering pattern for smooth UI:
855
973
 
@@ -857,7 +975,7 @@ Two-pass rendering pattern for smooth UI:
857
975
  import { getDailyPanchang } from 'panchang-ts';
858
976
  import { InteractionManager } from 'react-native';
859
977
 
860
- // Pass 1 cheapest useful result: elements, slots, muhurtas (~0.25 ms).
978
+ // Pass 1 (cheapest useful result): elements, slots, muhurtas (~0.25 ms).
861
979
  const fast = getDailyPanchang(date, location, {
862
980
  timezone: 330,
863
981
  sections: [],
@@ -865,7 +983,7 @@ const fast = getDailyPanchang(date, location, {
865
983
  });
866
984
  setState(fast);
867
985
 
868
- // Pass 2 background, everything (~0.41 ms).
986
+ // Pass 2 (background): everything (~0.41 ms).
869
987
  InteractionManager.runAfterInteractions(() => {
870
988
  setState(getDailyPanchang(date, location, { timezone: 330 }));
871
989
  });
@@ -877,35 +995,35 @@ InteractionManager.runAfterInteractions(() => {
877
995
 
878
996
  📖 [Full accuracy notes →](https://dharmagya.app/docs/panchang-ts/accuracy)
879
997
 
880
- 8,772 tests across 137 files, including fixtures cross-verified against reference
881
- panchang calculations spanning 20252026 across 10 Indian cities plus New York,
998
+ 8,805 tests across 138 files, including fixtures cross-verified against reference
999
+ panchang calculations spanning 2025-2026 across 10 Indian cities plus New York,
882
1000
  London, Sydney, Dubai, Singapore (diaspora fixtures cover DST on
883
1001
  `America/New_York`).
884
1002
 
885
1003
  | Element | Accuracy |
886
1004
  |---|---|
887
1005
  | Sunrise / Sunset | ≤29 s observed vs reference minute-midpoint (±45 s tolerance) |
888
- | Moonrise / Moonset | Meeus apparent-upper-limb (refraction + parallax); ~35 min vs simpler-horizon authorities is expected |
1006
+ | Moonrise / Moonset | Meeus apparent-upper-limb (refraction + parallax); ~3-5 min vs simpler-horizon authorities is expected |
889
1007
  | Tithi / Nakshatra / Yoga / Karana names | Exact match vs reference |
890
- | Tithi / Nakshatra / Yoga / Karana end-times | ≤60 s vs Drik across all 20 audited comparisons |
891
- | Ayanamsa (Lahiri) | Reproduces DrikPanchang's published value to ~0.01″ across 19502050 |
892
- | Planetary positions (SunSaturn) | ±0.02° sidereal |
1008
+ | Tithi / Nakshatra / Yoga / Karana end-times | ≤60 s vs the reference almanac across all 20 audited comparisons |
1009
+ | Ayanamsa (Lahiri) | Reproduces the reference almanac's published value to ~0.01″ across 1950-2050 |
1010
+ | Planetary positions (Sun-Saturn) | ±0.02° sidereal |
893
1011
  | Planetary positions (Rahu/Ketu) | ≤0.5° mean node, ≤0.6° true node (typical) |
894
1012
  | Lagna sidereal longitude | Cross-checked against Jagannath Hora reference charts |
895
1013
  | D1 / D9 house placement | Exact match vs reference for 9-graha placement |
896
1014
  | Ashtakoot total | ±1 point per pair across 30+ matched pairs |
897
- | Varjyam windows | count + position vs DrikPanchang over a 61-day / two-nakshatra-cycle sweep, ≤2 min (62/62 windows) |
898
- | Sade Sati arc start/end | ±12 days vs authoritative ephemerides |
1015
+ | Varjyam windows | count + position vs the reference almanac over a 61-day / two-nakshatra-cycle sweep, ≤2 min (62/62 windows) |
1016
+ | Sade Sati arc start/end | ±1-2 days vs authoritative ephemerides |
899
1017
 
900
1018
  **Festival dating** uses tithi-at-sunrise; a few festivals have authorities on
901
1019
  other rules (tithi-at-midnight for Janmashtami / Shivaratri / Diwali,
902
1020
  madhyahna-vyapini for Ganesh Chaturthi edge years) where output can drift
903
- ±1 day the exact list is
1021
+ ±1 day. The exact list is
904
1022
  [documented](https://dharmagya.app/docs/panchang-ts/accuracy#festival-tradeoff).
905
1023
 
906
1024
  **Detection conventions:** Aadal / Vidaal follow the classical Moon-from-Sun
907
1025
  nakshatra-distance rule, not the Tamil-Vakya weekday rule. Varjyam lists every
908
- window whose start falls in the Hindu day (Drik's attribution; Mula carries two
1026
+ window whose start falls in the Hindu day (the almanac's attribution; Mula carries two
909
1027
  tyajya spells). Do Ghati does not rotate by weekday.
910
1028
 
911
1029
  ---
@@ -914,7 +1032,7 @@ tyajya spells). Do Ghati does not rotate by weekday.
914
1032
 
915
1033
  📖 [Full performance notes →](https://dharmagya.app/docs/panchang-ts/performance)
916
1034
 
917
- Measured at Pune, Apple M-series, Node median of 11 processes. **Distinct
1035
+ Measured at Pune, Apple M-series, Node (median of 11 processes). **Distinct
918
1036
  days** is the calendar-scan cost; **same day repeated** is what a UI
919
1037
  re-rendering one date sees. Last column is published 4.3.1, benchmarked beside
920
1038
  this release.
@@ -927,12 +1045,12 @@ this release.
927
1045
 
928
1046
  **A default day is ~15× cheaper than 4.3.1**, a repeated day ~37×. The levers:
929
1047
 
930
- - **`sections`** skip the optional ephemeris-backed blocks (`'festivals'`,
1048
+ - **`sections`** skips the optional ephemeris-backed blocks (`'festivals'`,
931
1049
  `'eclipse'`, `'moonTimes'`, `'lunarWindows'`). Narrowing is exactly
932
1050
  output-neutral: every field a narrowed call computes is identical to the full
933
1051
  call's; omitted sections sit at their documented `null` / `[]`.
934
- - **`computeEndTimes: false`** drops the `endTime` transition searches;
935
- ~510% use it to drop fields you don't want, not to go faster.
1052
+ - **`computeEndTimes: false`** drops the `endTime` transition searches;
1053
+ ~5-10%. Use it to drop fields you don't want, not to go faster.
936
1054
 
937
1055
  Range helpers narrow internally: `computeEkadashiDatesForYear` **~18 ms/year**
938
1056
  (4.3.1: ~2,360), `computeFestivalsInRange` **~131 ms/year** (~2,180),
@@ -941,7 +1059,7 @@ cached process-wide (bounded, 20k entries), which also makes them
941
1059
  single-valued.
942
1060
 
943
1061
  One deliberate regression: raw chart primitives (`computeRashiChart`,
944
- `computeNavamsa`) cost ~0.32 ms vs ~0.10 in 4.3.1 the price of an
1062
+ `computeNavamsa`) cost ~0.32 ms vs ~0.10 in 4.3.1, the price of an
945
1063
  order-of-magnitude accuracy gain against JPL DE441. `computeShadbala` /
946
1064
  `computeBhavaBala` went the other way, ~2× faster.
947
1065
 
@@ -970,7 +1088,7 @@ Error codes: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
970
1088
  `CIRCUMPOLAR` (Placidus-KP houses above ±66.5°).
971
1089
 
972
1090
  **Polar locations.** `getDailyPanchang` and `getInstantPanchang` return `null`
973
- rather than throwing the Hindu day is undefined when sunrise can't be
1091
+ rather than throwing: the Hindu day is undefined when sunrise can't be
974
1092
  computed. The low-level `getSunrise` / `getSunset` primitives still throw
975
1093
  `PanchangError(NO_SUNRISE)` / `PanchangError(NO_SUNSET)` for direct callers
976
1094
  who need the precise reason. `getMoonrise` / `getMoonset` return `null` (normal
@@ -995,7 +1113,7 @@ dependencies**, and is `sideEffects: false`.
995
1113
 
996
1114
  ## Acknowledgements
997
1115
 
998
- As of v5 the package has **no runtime dependencies** the ephemeris, ΔT model
1116
+ As of v5 the package has **no runtime dependencies**: the ephemeris, ΔT model
999
1117
  and event searches are the library's own. Two projects still deserve credit:
1000
1118
  [astronomy-engine](https://github.com/cosinekitty/astronomy) by Don Cross (MIT),
1001
1119
  the runtime engine through 4.x and now the dev-time baseline the own ephemeris