panchang-ts 5.0.0 → 5.1.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.
- package/README.md +307 -1189
- package/dist/index.cjs +859 -377
- package/dist/index.d.cts +264 -63
- package/dist/index.d.ts +264 -63
- package/dist/index.js +858 -377
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
# panchang-ts
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/panchang-ts)
|
|
4
|
+
[](./LICENSE)
|
|
4
5
|
|
|
5
6
|
Pure TypeScript Hindu Panchang (almanac), Jyotish, and Birth Chart calculations.
|
|
6
|
-
Zero
|
|
7
|
+
Zero runtime dependencies. Works offline in React Native (Hermes), Node.js, and browsers.
|
|
7
8
|
|
|
8
|
-
**Fast** (~0.25 ms trimmed, ~0.41 ms full) · **Typed** (full TypeScript) · **Offline** (pure JS math) · **8,
|
|
9
|
+
**Fast** (~0.25 ms trimmed, ~0.41 ms full) · **Typed** (full TypeScript) · **Offline** (pure JS math) · **8,737 tests across 136 files**
|
|
10
|
+
|
|
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.
|
|
9
15
|
|
|
10
16
|
---
|
|
11
17
|
|
|
@@ -124,6 +130,142 @@ Ekadashi split. For reliable festival dating, use `getDailyPanchang`.
|
|
|
124
130
|
|
|
125
131
|
---
|
|
126
132
|
|
|
133
|
+
## Upgrading from 5.1 (unreleased)
|
|
134
|
+
|
|
135
|
+
Two deliberate type breaks, both correcting fields that did not match
|
|
136
|
+
DrikPanchang (the project's parity oracle), in the same style as 5.1's
|
|
137
|
+
`varjyam` break.
|
|
138
|
+
|
|
139
|
+
### `inauspicious.durMuhurta` is now `DurMuhurtaPeriod[]`
|
|
140
|
+
|
|
141
|
+
```diff
|
|
142
|
+
- const [dm1, dm2] = r.inauspicious.durMuhurta;
|
|
143
|
+
+ r.inauspicious.durMuhurta.forEach((dm) => show(dm)); // 1–2 windows
|
|
144
|
+
+ // dm.segment is 'day' or 'night' (only Tuesday carries a night window)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The old table emitted two day windows every day and matched drik on none of
|
|
148
|
+
the seven weekdays. The corrected classical Muhurta-Chintamani table gives
|
|
149
|
+
one window on Sunday and Wednesday, two elsewhere — and Tuesday's second
|
|
150
|
+
window falls at **night** (the 7th of the 15 sunset→sunrise muhurtas), so
|
|
151
|
+
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.
|
|
153
|
+
|
|
154
|
+
### `muhurtas.amritKala` is now `TimePeriod[]`
|
|
155
|
+
|
|
156
|
+
```diff
|
|
157
|
+
- if (r.muhurtas.amritKala) show(r.muhurtas.amritKala);
|
|
158
|
+
+ r.muhurtas.amritKala.forEach(show); // [] when the day has none
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Amrit Kala shares Varjyam's architecture (drik prints them from the same
|
|
162
|
+
frame): each window anchors at its nakshatra's own start, offset by a
|
|
163
|
+
per-nakshatra count of nakshatra-elastic ghatikas, spans exactly 4 such
|
|
164
|
+
ghatikas, and belongs to the Hindu day its **start** falls in — 0–2 windows
|
|
165
|
+
per day. The old sunrise-anchored single window disagreed with drik by up
|
|
166
|
+
to ~16 h. `computeAmritKala` is replaced by
|
|
167
|
+
`computeAmritKalaWindows(sunriseUtc, nextSunriseUtc, getMoon)`.
|
|
168
|
+
|
|
169
|
+
### Ekadashi splits: Smarta first, Vaishnava second
|
|
170
|
+
|
|
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
|
|
173
|
+
Ekadashi date-time page. Two corrections bring the library in line:
|
|
174
|
+
|
|
175
|
+
```diff
|
|
176
|
+
// Dashami-viddha day (e.g. Rama Ekadashi, 2027-10-25)
|
|
177
|
+
- ['vaishnava_ekadashi', 'smarta_ekadashi' /* deferred */, 'ekadashi']
|
|
178
|
+
+ ['smarta_ekadashi', 'ekadashi'] // vaishnava_ekadashi is tomorrow
|
|
179
|
+
|
|
180
|
+
// First day of a vriddha Dwadashi (e.g. 2026-08-24)
|
|
181
|
+
- []
|
|
182
|
+
+ ['vaishnava_ekadashi']
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Both the Dashami-viddha and the vriddha-Dwadashi (Pakshavardhini) splits now
|
|
186
|
+
match drik across every pair it publishes in 2024–2028. `getDailyPanchang`
|
|
187
|
+
callers that keyed off `smarta_ekadashi` / `vaishnava_ekadashi` on split days
|
|
188
|
+
will see the two swap places; `computeEkadashiDatesForYear` is unchanged.
|
|
189
|
+
Custom locale packs need the renamed viddha description keys — see the
|
|
190
|
+
CHANGELOG.
|
|
191
|
+
|
|
192
|
+
### Regional solar new years land on their own days
|
|
193
|
+
|
|
194
|
+
`vishu`, `baisakhi` and `pohela_boishakh` no longer share Puthandu's day.
|
|
195
|
+
Each keys off the Mesha transit moment its own way, so in 2027 Vishu and
|
|
196
|
+
Pohela Boishakh fall on April 15 while Puthandu falls on April 14, and in
|
|
197
|
+
2028 Vaisakhi falls on April 13 while the rest fall on April 14.
|
|
198
|
+
`getHinduNewYear(year, region, …)` follows the same per-region rules.
|
|
199
|
+
|
|
200
|
+
Several other **value-level** corrections ride along without shape changes:
|
|
201
|
+
dur muhurta ordinals, night choghadiya names, Bhadra vasa (now Moon-rashi
|
|
202
|
+
keyed, with a piecewise `vasa` segment list on `BhadraInfo`), night-transit
|
|
203
|
+
Sankranti dates (+ a new `moment` field on `SankrantiEvent`), kshaya-Dwadashi
|
|
204
|
+
Ekadashi advance, Vijayadashami/Karva Chauth/Janmashtami kala rules, the
|
|
205
|
+
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.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Upgrading from 5.0
|
|
212
|
+
|
|
213
|
+
One deliberate type break, two corrected dasha tables, and one opt-in flag.
|
|
214
|
+
|
|
215
|
+
### `inauspicious.varjyam` is now `TimePeriod[]`
|
|
216
|
+
|
|
217
|
+
```diff
|
|
218
|
+
- if (r.inauspicious.varjyam) show(r.inauspicious.varjyam);
|
|
219
|
+
+ r.inauspicious.varjyam.forEach(show); // [] when the day has none
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
5.0 evaluated only the nakshatra active at sunrise and dropped the second
|
|
223
|
+
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
|
|
225
|
+
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
|
|
227
|
+
land after next sunrise. Validated window-for-window against a 61-day
|
|
228
|
+
DrikPanchang sweep (Aug–Sep 2026, two full nakshatra cycles): 62/62 match.
|
|
229
|
+
|
|
230
|
+
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
|
|
232
|
+
*Muhurta* concur), so Mula days now emit the window 5.0 missed; and the
|
|
233
|
+
standalone `computeVarjyam` primitive returns the *earliest* of a nakshatra's
|
|
234
|
+
spells overlapping the day. New export: `computeVarjyamWindows(sunriseUtc,
|
|
235
|
+
nextSunriseUtc, getMoon)`.
|
|
236
|
+
|
|
237
|
+
### Yogini and Ashtottari starting lords were wrong — now classical
|
|
238
|
+
|
|
239
|
+
- **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 →
|
|
242
|
+
Dhanya. Verified against published worked examples and PyJHora.
|
|
243
|
+
- **Ashtottari** used a years-proportional split of the zodiac from a Krittika
|
|
244
|
+
anchor, matching no source. Now the classical Ardradi **group table**
|
|
245
|
+
(malefics rule four nakshatras each, benefics three; Sun = Ardra…Ashlesha,
|
|
246
|
+
Venus = Krittika…Mrigashira; exported as `ASHTOTTARI_NAKSHATRA_GROUPS`),
|
|
247
|
+
with the balance from the elapsed fraction of the group. Verified against
|
|
248
|
+
PyJHora and Maitreya 8, which agree on every output.
|
|
249
|
+
|
|
250
|
+
Both functions keep their signatures; recorded outputs from 5.0 will differ
|
|
251
|
+
and should be discarded.
|
|
252
|
+
|
|
253
|
+
### Opt-in Gana-dosha cancellation in `computeAshtakoot`
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
computeAshtakoot(boy, girl, { ganaCancellation: true });
|
|
257
|
+
```
|
|
258
|
+
|
|
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
|
|
261
|
+
standard). With the flag raised, a doshic Gana score (≤ 1) is restored to the
|
|
262
|
+
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
|
|
264
|
+
independent pandit corpora; weaker ones are documented on `AshtakootOptions`
|
|
265
|
+
and deliberately not encoded.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
127
269
|
## Upgrading from 4.x
|
|
128
270
|
|
|
129
271
|
Three changes move numbers that 4.x produced, and one option is gone.
|
|
@@ -318,10 +460,8 @@ a `*Local` companion: `sun.riseLocal`, `inauspicious.rahuKalam.startLocal`,
|
|
|
318
460
|
timezone, so there is no zone to render a wall clock in.
|
|
319
461
|
|
|
320
462
|
**Cost:** rendering the strings adds ~0.04 ms per daily panchang — invisible on
|
|
321
|
-
a cold call
|
|
322
|
-
|
|
323
|
-
change landed, mid-Phase-36; the release finally warms to **0.17 ms**, against
|
|
324
|
-
published 4.3.1's **6.20 ms**.
|
|
463
|
+
a cold call and ~20% of a fully cached warm one. The release warms to
|
|
464
|
+
**0.17 ms**, against published 4.3.1's **6.20 ms**.
|
|
325
465
|
|
|
326
466
|
### `result.timezone` is now an object
|
|
327
467
|
|
|
@@ -391,7 +531,8 @@ For `getInstantPanchang`: `tithi` / `nakshatra` / `yoga` / `karana` / `vara` →
|
|
|
391
531
|
`| null` for `bhadra` / `varjyam` / `eclipse`, `?`-optional for `chandraBalam` /
|
|
392
532
|
`tarabala`, and an empty array for `panchakaRahita` / `festivals`. v5 has one
|
|
393
533
|
rule: **every field is always present**, a value that does not apply is `null`,
|
|
394
|
-
and a collection that does not apply is `[]`.
|
|
534
|
+
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).)
|
|
395
536
|
|
|
396
537
|
```diff
|
|
397
538
|
- if ('chandraBalam' in r) … // 4.x: field absent without janmaRashi
|
|
@@ -485,1109 +626,223 @@ the **lunar** one, which was previously left to guesswork.
|
|
|
485
626
|
|
|
486
627
|
# Feature Reference
|
|
487
628
|
|
|
488
|
-
|
|
489
|
-
|
|
629
|
+
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.
|
|
631
|
+
|
|
632
|
+
## Pancha Anga & the daily result
|
|
490
633
|
|
|
491
|
-
|
|
634
|
+
📖 [Daily Panchang →](https://dharmagya.app/docs/panchang-ts/daily-panchang)
|
|
492
635
|
|
|
493
636
|
```typescript
|
|
494
637
|
const r = getDailyPanchang(date, location, { timezone: 330 })!;
|
|
495
638
|
|
|
496
|
-
r.angas.tithis.forEach(t => console.log(t.name, t.paksha, t.
|
|
497
|
-
r.angas.
|
|
498
|
-
r.
|
|
499
|
-
r.
|
|
500
|
-
|
|
639
|
+
r.angas.tithis.forEach(t => console.log(t.name, t.paksha, t.endTime));
|
|
640
|
+
r.angas.vara.name; // "Mangalawara"
|
|
641
|
+
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
|
|
644
|
+
r.periods.choghadiya.day[0].name; // 16 Choghadiya + Gowri + 24 Hora slots
|
|
645
|
+
r.anandadiYoga.name; r.specialYogas; // Anandadi + Amrit/Sarvartha Siddhi, …
|
|
501
646
|
|
|
502
647
|
// Single-instant snapshot:
|
|
503
648
|
import { getInstantPanchang } from 'panchang-ts';
|
|
504
649
|
const i = getInstantPanchang(new Date(), location)!;
|
|
505
|
-
console.log(i.angas.tithi.name, i.angas.nakshatra.name
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
## Lunar & Solar Calendar
|
|
509
|
-
|
|
510
|
-
```typescript
|
|
511
|
-
const r = getDailyPanchang(date, loc, { timezone: 330, masaSystem: 'purnimanta' })!;
|
|
512
|
-
|
|
513
|
-
r.calendar.chandramasa.name; // active system (default: Purnimanta / North Indian)
|
|
514
|
-
r.calendar.chandramasa.amantaName; // South Indian
|
|
515
|
-
r.calendar.chandramasa.purnimantaName; // North Indian
|
|
516
|
-
r.calendar.chandramasa.isAdhika; // true during leap months
|
|
517
|
-
r.calendar.samvat.vikramSamvat; // 2081
|
|
518
|
-
r.calendar.samvat.shakaSamvat; // 1946
|
|
519
|
-
|
|
520
|
-
r.calendar.masa.name; // current solar month (Mesha … Meena)
|
|
521
|
-
r.sun.nakshatra.name; // Sun's nakshatra
|
|
522
|
-
r.moon.rashi.name; // Moon sign
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
## Sun, Moon & Muhurta
|
|
526
|
-
|
|
527
|
-
```typescript
|
|
528
|
-
import { getSunrise, getSunset, getMoonrise, getMoonset } from 'panchang-ts';
|
|
529
|
-
|
|
530
|
-
const sunrise = getSunrise(localMidnightUtc, loc);
|
|
531
|
-
const sunset = getSunset(sunrise, loc);
|
|
532
|
-
const moonrise = getMoonrise(localMidnightUtc, loc); // null on some days (normal)
|
|
533
|
-
const moonset = getMoonset(localMidnightUtc, loc);
|
|
534
|
-
|
|
535
|
-
// Or read off the daily result:
|
|
536
|
-
const r = getDailyPanchang(date, loc, { timezone: 330 })!;
|
|
537
|
-
r.sun.rise; r.sun.set; r.moon.rise; r.moon.set; r.sun.nextRise;
|
|
538
|
-
r.sun.dayDurationMinutes; r.sun.nightDurationMinutes;
|
|
539
|
-
|
|
540
|
-
// Auspicious muhurtas
|
|
541
|
-
r.muhurtas.brahma; // two muhurtas before sunrise
|
|
542
|
-
r.muhurtas.abhijit; // 8th day-muhurta; null on Wednesday (Drik convention)
|
|
543
|
-
r.muhurtas.vijaya; // 11th day-muhurta
|
|
544
|
-
r.muhurtas.godhuli; // "cow-dust" sunset muhurta
|
|
545
|
-
r.muhurtas.nishita; // midnight muhurta (Shivaratri)
|
|
546
|
-
r.muhurtas.madhyahna; // solar noon ±24 min
|
|
547
|
-
r.muhurtas.pratahSandhya; // dawn twilight, ends at sunrise
|
|
548
|
-
r.muhurtas.sayahnaSandhya; // dusk twilight, starts at sunset
|
|
549
|
-
r.muhurtas.amritKala; // nakshatra-specific window (null when nakshatra has none)
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
`muhurtas.pratahSandhya` / `muhurtas.sayahnaSandhya` width =
|
|
553
|
-
`sun.nightDurationMinutes / 10` (~62–81 min).
|
|
554
|
-
|
|
555
|
-
## Inauspicious Periods
|
|
556
|
-
|
|
557
|
-
```typescript
|
|
558
|
-
const r = getDailyPanchang(date, loc, { timezone: 330 })!;
|
|
559
|
-
|
|
560
|
-
r.inauspicious.rahuKalam; // { start, end }
|
|
561
|
-
r.inauspicious.gulikaKalam;
|
|
562
|
-
r.inauspicious.yamaganda;
|
|
563
|
-
r.inauspicious.durMuhurta; // two ~48-min windows
|
|
564
|
-
r.inauspicious.varjyam; // { start, end } | null
|
|
565
|
-
r.inauspicious.gandaMula; // { active, severity: 'mild'|'severe'|null, ... }
|
|
566
|
-
r.inauspicious.bhadra; // { start, end, location: 'earth'|'heaven'|'paatal', isActive } | null
|
|
567
|
-
r.inauspicious.panchaka; // boolean — Moon in last 5 nakshatras
|
|
568
|
-
r.inauspicious.panchakaInfo; // which of the five, and whether it's a dosha
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
Panchaka is not one undifferentiated affliction. The tradition names five and
|
|
572
|
-
picks between them by **the weekday the spell began on** — so the type belongs
|
|
573
|
-
to the spell, not the day, and two days with identical tithi, nakshatra and
|
|
574
|
-
vara can carry different ones. A spell begun on a Wednesday or Thursday
|
|
575
|
-
(`'samanya'`) carries no named affliction at all:
|
|
576
|
-
|
|
577
|
-
```typescript
|
|
578
|
-
const pk = r.inauspicious.panchakaInfo;
|
|
579
|
-
if (pk.active && pk.isDosha) {
|
|
580
|
-
console.log(pk.name, '— began on vara', pk.onsetVara); // e.g. "Mrityu Panchaka"
|
|
581
|
-
}
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
## Time-Slot Systems
|
|
585
|
-
|
|
586
|
-
```typescript
|
|
587
|
-
const r = getDailyPanchang(date, loc, { timezone: 330 })!;
|
|
588
|
-
|
|
589
|
-
// Choghadiya — 8 day + 8 night named, rated slots (Amrit, Kaal, Shubh, Rog, …)
|
|
590
|
-
r.periods.choghadiya.day.forEach(s => console.log(s.name, s.qualityName, s.start, s.end));
|
|
591
|
-
|
|
592
|
-
// Gowri Panchangam ("Nalla Neram") — 8 day + 8 night Tamil slots
|
|
593
|
-
r.periods.gowri.day.forEach(s => console.log(s.name, s.qualityName));
|
|
594
|
-
|
|
595
|
-
// Hora — 12 day + 12 night planetary hours (Chaldean order)
|
|
596
|
-
r.periods.hora.day.forEach(h => console.log(h.planet, h.start, h.end));
|
|
597
|
-
|
|
598
|
-
// Do Ghati Muhurta — 15 day + 15 night ~48-min deity-keyed slots (no vara rotation)
|
|
599
|
-
r.muhurtas.doGhati.day.forEach(g => console.log(g.name, g.start, g.end));
|
|
600
|
-
|
|
601
|
-
// Panchaka Rahita — slices of the day FREE of Panchaka ([] when it pervades)
|
|
602
|
-
r.inauspicious.panchakaRahita.forEach(slice => console.log(slice.start, slice.end));
|
|
603
|
-
```
|
|
604
|
-
|
|
605
|
-
## Special Yogas
|
|
606
|
-
|
|
607
|
-
```typescript
|
|
608
|
-
const r = getDailyPanchang(date, loc, { timezone: 330 })!;
|
|
609
|
-
|
|
610
|
-
r.anandadiYoga.name; // 28-cycle name e.g. "Ananda"
|
|
611
|
-
r.specialYogas.forEach(y => {
|
|
612
|
-
// type: amrit_siddhi | sarvartha_siddhi | ravi_pushya | guru_pushya
|
|
613
|
-
// | dwipushkar | tripushkar | jwalamukhi (inauspicious)
|
|
614
|
-
// | aadal | vidaal | ravi (Moon-from-Sun nakshatra-distance rules)
|
|
615
|
-
console.log(y.name, y.type);
|
|
616
|
-
});
|
|
650
|
+
console.log(i.angas.tithi.name, i.angas.nakshatra.name);
|
|
617
651
|
```
|
|
618
652
|
|
|
619
653
|
## Festivals (80+)
|
|
620
654
|
|
|
621
|
-
|
|
622
|
-
fast emits a `deferralDate` for Dwadashi), Pradosha, Sankranti + regional
|
|
623
|
-
variants (Pongal, Vishu, Baisakhi, Pohela Boishakh, Bihu, Uttarayan, Lohri…),
|
|
624
|
-
canonical-time classical (Janmashtami, Shivaratri, Ganesh Chaturthi, Diwali,
|
|
625
|
-
Holi, Raksha Bandhan — Bhadra-aware, Karva Chauth, Akshaya Tritiya…),
|
|
626
|
-
regional (Gudi Padwa, Gangaur, Teej variants, Onam, Chhath…), monthly
|
|
627
|
-
observances (Masik Shivaratri, Pushya days, Shravan Somvar…).
|
|
655
|
+
📖 [Festivals →](https://dharmagya.app/docs/panchang-ts/festivals)
|
|
628
656
|
|
|
629
657
|
```typescript
|
|
630
|
-
r.festivals.forEach(f =>
|
|
631
|
-
|
|
632
|
-
// type: major | minor | ekadashi | smarta_ekadashi | vaishnava_ekadashi
|
|
633
|
-
// | pradosha | sankranti | eclipse
|
|
634
|
-
console.log(f.key, f.name, f.type, f.deferralDate);
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
// `name` is localized, so match on `key` — never on `name`.
|
|
658
|
+
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`:
|
|
638
660
|
const hasDiwali = r.festivals.some(f => f.key === 'diwali');
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
`key` is on engine results (`getDailyPanchang`, `getInstantPanchang`,
|
|
642
|
-
`computeFestivalsInRange`). Entries read back out of a `buildFestivalsTable` table
|
|
643
|
-
carry `name` / `type` / `description` only.
|
|
644
|
-
|
|
645
|
-
### Regional scoping
|
|
646
661
|
|
|
647
|
-
|
|
648
|
-
|
|
662
|
+
// Scope regional variants: 21 state slugs + 'nepal' + 'all' (default)
|
|
663
|
+
getDailyPanchang(jan14, chennai, { timezone: 330, region: 'tamil-nadu' });
|
|
649
664
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
// → ["Sankranti","Makar Sankranti","Pongal","Uttarayan","Magh Bihu","Ayyappa Makara Jyothi"]
|
|
654
|
-
|
|
655
|
-
// Tamil Nadu only:
|
|
656
|
-
getDailyPanchang(jan14, chennai, { timezone: 330, region: 'tamil-nadu' })!
|
|
657
|
-
.festivals.map(f => f.name);
|
|
658
|
-
// → ["Sankranti","Makar Sankranti","Pongal"]
|
|
659
|
-
|
|
660
|
-
// Lohri fires on the Hindu day BEFORE Makara transit, in Punjab/Haryana/Himachal scope:
|
|
661
|
-
getDailyPanchang(jan13, amritsar, { timezone: 330, region: 'punjab' })!
|
|
662
|
-
.festivals.some(f => f.name === 'Lohri'); // true
|
|
665
|
+
// Pre-computed table: build once, cache the JSON, read engine-free.
|
|
666
|
+
import { buildFestivalsTable } from 'panchang-ts';
|
|
667
|
+
import { readFestivalsForYear, readFestivalsForDate } from 'panchang-ts/festivals';
|
|
663
668
|
```
|
|
664
669
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
### Pre-computed table — build your own and cache it
|
|
670
|
+
No table ships with the package — festival dates are observer-dependent, so you
|
|
671
|
+
build one for your users' location and years (`npm run festivals:gen` is a
|
|
672
|
+
worked example).
|
|
670
673
|
|
|
671
|
-
|
|
672
|
-
table once with `buildFestivalsTable`, cache the JSON, and read it back through
|
|
673
|
-
the engine-free `panchang-ts/festivals` entry point.
|
|
674
|
+
## Eclipses & Moon Phases
|
|
674
675
|
|
|
675
|
-
|
|
676
|
-
observer-dependent — canonical times (nishita / pradosha / chandrodaya …) shift
|
|
677
|
-
with the timezone offset, so a table built for one place can be ±1 day wrong
|
|
678
|
-
elsewhere — and any table baked into the package would also go stale. Building
|
|
679
|
-
your own means it is correct for *your* users and covers whatever years you
|
|
680
|
-
want.
|
|
676
|
+
📖 [Eclipses & Moon Phases →](https://dharmagya.app/docs/panchang-ts/eclipses-moon-phases)
|
|
681
677
|
|
|
682
678
|
```typescript
|
|
683
|
-
import { buildFestivalsTable } from 'panchang-ts'; // uses the engine
|
|
684
|
-
import {
|
|
685
|
-
readFestivalsForYear,
|
|
686
|
-
readFestivalsForDate,
|
|
687
|
-
readFestivalsYearRange,
|
|
688
|
-
} from 'panchang-ts/festivals'; // engine-free
|
|
689
|
-
|
|
690
|
-
// Build once — at your build time, or on first launch in the background.
|
|
691
|
-
const table = buildFestivalsTable({
|
|
692
|
-
location: { latitude: 25.3176, longitude: 82.9739 }, // Varanasi
|
|
693
|
-
timezoneOffsetMinutes: 330, // IST; -300 = US Eastern, 0 = UK
|
|
694
|
-
startYear: 2024,
|
|
695
|
-
endYear: 2031,
|
|
696
|
-
languages: ['en', 'hi'], // drop 'hi' to halve the size
|
|
697
|
-
referenceLocation: 'Varanasi',
|
|
698
|
-
});
|
|
699
|
-
// …persist `table` as JSON (disk / MMKV / your bundler's asset pipeline).
|
|
700
|
-
|
|
701
|
-
// Later reads are instant lookups — no engine, no ephemeris.
|
|
702
|
-
readFestivalsYearRange(table); // { start: 2024, end: 2031 }
|
|
703
|
-
readFestivalsForYear(table, 2026)!.length; // ~150 festival days
|
|
704
|
-
const diwali = readFestivalsForYear(table, 2026)!
|
|
705
|
-
.find(d => d.festivals.some(f => f.name === 'Diwali'))!.date;
|
|
706
|
-
readFestivalsForDate(table, diwali); // [Narak Chaturdashi, Diwali]
|
|
707
|
-
readFestivalsForDate(table, diwali, 'hi'); // [नरक चतुर्दशी, दिवाली]
|
|
708
|
-
```
|
|
709
|
-
|
|
710
|
-
`panchang-ts/festivals` imports no astronomy code, so a client bundle that only
|
|
711
|
-
*reads* a table never pulls in the engine. Keep `buildFestivalsTable` on the
|
|
712
|
-
build/server side (or behind a one-time on-device warm-up) and ship only the
|
|
713
|
-
JSON.
|
|
714
|
-
|
|
715
|
-
Eclipses are excluded here — visibility is location-dependent, so they get their
|
|
716
|
-
own table at `panchang-ts/eclipses` (see [Eclipses](#eclipses)).
|
|
717
|
-
|
|
718
|
-
`npm run festivals:gen` is a worked example of the whole pattern; it writes a
|
|
719
|
-
rolling 2-past / 5-future window to `./festivals.json` (or a path you pass).
|
|
720
|
-
|
|
721
|
-
**Other notes:** Karva Chauth / Dhanteras / Diwali emit with Purnimanta paksha
|
|
722
|
-
naming.
|
|
723
|
-
|
|
724
|
-
## Eclipses
|
|
725
|
-
|
|
726
|
-
```typescript
|
|
727
|
-
const r = getDailyPanchang(date, loc, { timezone: 330 })!;
|
|
728
679
|
if (r.eclipse) {
|
|
729
|
-
r.eclipse.kind;
|
|
730
|
-
r.eclipse.
|
|
731
|
-
r.eclipse.
|
|
732
|
-
r.eclipse.magnitude; // catalogue magnitude — DIAMETER fraction;
|
|
733
|
-
// >1 when total, negative when penumbral
|
|
734
|
-
r.eclipse.visibleFromLocation; // body above horizon at peak?
|
|
735
|
-
r.eclipse.start; r.eclipse.peak; r.eclipse.end;
|
|
680
|
+
r.eclipse.kind; r.eclipse.subtype; // 'solar'|'lunar', 'partial'|'total'|…
|
|
681
|
+
r.eclipse.obscuration; // disc AREA covered, 0..1
|
|
682
|
+
r.eclipse.magnitude; // catalogue DIAMETER fraction
|
|
736
683
|
r.eclipse.sutakStart; r.eclipse.sutakEnd;
|
|
737
|
-
// Sutak: 12 h (4 prahara) before solar, 9 h (3 prahara) before lunar
|
|
738
684
|
}
|
|
739
685
|
|
|
740
|
-
import { getUpcomingSolarEclipse,
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
### Pre-computed table — build your own and cache it
|
|
745
|
-
|
|
746
|
-
Same pattern as festivals: build a table with `buildEclipsesTable`, cache it,
|
|
747
|
-
read it back through the engine-free `panchang-ts/eclipses` entry point.
|
|
748
|
-
|
|
749
|
-
**No table is bundled.** Which eclipses are visible — and therefore which carry
|
|
750
|
-
`sutak` — is location-dependent, so a table is only meaningful for the place it
|
|
751
|
-
was built for.
|
|
686
|
+
import { getUpcomingSolarEclipse, computeMoonPhasesInRange } from 'panchang-ts';
|
|
687
|
+
getUpcomingSolarEclipse(new Date(), loc, 365);
|
|
688
|
+
computeMoonPhasesInRange(start, end); // precise new/quarter/full instants
|
|
752
689
|
|
|
753
|
-
|
|
754
|
-
import { buildEclipsesTable } from 'panchang-ts'; // uses the engine
|
|
755
|
-
import {
|
|
756
|
-
readEclipsesForYear,
|
|
757
|
-
readEclipsesForDate,
|
|
758
|
-
readEclipsesYearRange,
|
|
759
|
-
} from 'panchang-ts/eclipses'; // engine-free
|
|
760
|
-
|
|
761
|
-
const table = buildEclipsesTable({
|
|
762
|
-
location: { latitude: 25.3176, longitude: 82.9739 }, // Varanasi
|
|
763
|
-
timezoneOffsetMinutes: 330,
|
|
764
|
-
startYear: 2024,
|
|
765
|
-
endYear: 2031,
|
|
766
|
-
languages: ['en', 'hi'],
|
|
767
|
-
// visibleOnly: false → also include eclipses below the horizon (no sutak)
|
|
768
|
-
});
|
|
769
|
-
// …persist `table` as JSON, then:
|
|
770
|
-
|
|
771
|
-
readEclipsesYearRange(table); // { start: 2024, end: 2031 }
|
|
772
|
-
const e = readEclipsesForYear(table, 2025)![0].eclipses[0];
|
|
773
|
-
e.kind; // 'lunar'
|
|
774
|
-
e.subtype; // 'total'
|
|
775
|
-
e.start; e.peak; e.end; // ISO UTC strings
|
|
776
|
-
e.obscuration; // 0..1 disc area covered at peak
|
|
777
|
-
e.magnitude; // catalogue magnitude (diameter); >1 total, <0 penumbral
|
|
778
|
-
e.visibleFromLocation; // visible during any phase?
|
|
779
|
-
e.visibleAtPeak; // is greatest eclipse itself above the horizon?
|
|
780
|
-
e.sutak; // { start, end } — see note below
|
|
781
|
-
readEclipsesForDate(table, '2025-09-07', 'hi'); // [पूर्ण चंद्र ग्रहण]
|
|
690
|
+
// Engine-free tables: panchang-ts/eclipses and panchang-ts/moon-phases
|
|
782
691
|
```
|
|
783
692
|
|
|
784
|
-
|
|
785
|
-
phase** (so one already in progress at moon/sunrise or moon/sunset is included);
|
|
786
|
-
`visibleAtPeak` tells you whether greatest eclipse itself is observable.
|
|
787
|
-
|
|
788
|
-
Solar eclipses report the subtype seen **locally** (a globally-total eclipse may
|
|
789
|
-
read `partial` from a given place). The `sutak` window is present only where it
|
|
790
|
-
applies — all visible solar eclipses and visible **umbral** (partial/total)
|
|
791
|
-
lunar eclipses; **penumbral** lunar eclipses carry no `sutak` and are not
|
|
792
|
-
religiously observed (drik / pandit consensus).
|
|
793
|
-
|
|
794
|
-
For one-off astronomical detail without building a table, use
|
|
795
|
-
`getUpcomingEclipses` / `computeEclipsesInRange` from the main entry.
|
|
796
|
-
|
|
797
|
-
`npm run eclipses:gen` is a worked example; it writes a rolling 2-past /
|
|
798
|
-
5-future window to `./eclipses.json` (or a path you pass).
|
|
799
|
-
|
|
800
|
-
## Moon Phases
|
|
693
|
+
## Muhurta Engine
|
|
801
694
|
|
|
802
|
-
|
|
803
|
-
**full** (Purnima), **last quarter** — as precise instants. (These are the
|
|
804
|
-
astronomical quarter moments, distinct from the same-named *tithis*, which are
|
|
805
|
-
~24h windows.)
|
|
695
|
+
📖 [Muhurta Engine →](https://dharmagya.app/docs/panchang-ts/muhurta)
|
|
806
696
|
|
|
807
697
|
```typescript
|
|
808
|
-
import {
|
|
809
|
-
const phases = computeMoonPhasesInRange(new Date('2026-01-01'), new Date('2026-12-31'));
|
|
810
|
-
phases.forEach(p => console.log(p.phase, p.time.toISOString())); // ~49 / year
|
|
811
|
-
```
|
|
812
|
-
|
|
813
|
-
### Pre-computed table — build your own and cache it
|
|
814
|
-
|
|
815
|
-
Same pattern again, at `panchang-ts/moon-phases`. Phases are **global instants**,
|
|
816
|
-
so `buildMoonPhasesTable` takes only a `timezoneOffsetMinutes` (no coordinates)
|
|
817
|
-
— the timezone just decides which calendar date each instant lands on (a new
|
|
818
|
-
moon at 19:52 UTC on Jan 18 is listed under Jan 19 in IST).
|
|
698
|
+
import { scoreMuhurta, computeAuspiciousDatesInRange, vivahRule } from 'panchang-ts';
|
|
819
699
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
readMoonPhasesForDate,
|
|
825
|
-
readMoonPhasesYearRange,
|
|
826
|
-
} from 'panchang-ts/moon-phases'; // engine-free
|
|
827
|
-
|
|
828
|
-
const table = buildMoonPhasesTable({
|
|
829
|
-
timezoneOffsetMinutes: 330, // IST; -300 = US Eastern
|
|
830
|
-
startYear: 2024,
|
|
831
|
-
endYear: 2031,
|
|
832
|
-
languages: ['en', 'hi'],
|
|
833
|
-
});
|
|
834
|
-
// …persist `table` as JSON, then:
|
|
700
|
+
const s = scoreMuhurta(new Date('2026-05-12'), DELHI, vivahRule, { timezone: 330 });
|
|
701
|
+
s.score; // 0..100; passes when ≥ 50
|
|
702
|
+
s.factors; // structured, stable codes — localize/filter on these
|
|
703
|
+
s.reasons; // diagnostic English
|
|
835
704
|
|
|
836
|
-
|
|
837
|
-
readMoonPhasesForYear(table, 2026)!.length; // ~49 phase days
|
|
838
|
-
readMoonPhasesForDate(table, '2026-01-03'); // [{ phase: 'full', name: 'Full Moon', … }]
|
|
839
|
-
readMoonPhasesForDate(table, '2026-01-03', 'hi'); // [{ phase: 'full', name: 'पूर्णिमा', … }]
|
|
705
|
+
computeAuspiciousDatesInRange(vivahRule, start, end, DELHI, { timezone: 330 });
|
|
840
706
|
```
|
|
841
707
|
|
|
842
|
-
|
|
708
|
+
13 stock rules (vivah, griha pravesh, namakarana, …) or your own pure-data
|
|
709
|
+
`MuhurtaRule`. Vara × Tithi yogas (Siddha, Amrita, Dagdha, …) are scored
|
|
710
|
+
jointly. Pre-compute a table with `buildMuhurtaTable` and read it back through
|
|
711
|
+
`panchang-ts/muhurta` (~1.7 KB, no astronomy code).
|
|
843
712
|
|
|
844
|
-
|
|
845
|
-
5-future window to `./moonPhases.json` (or a path you pass).
|
|
713
|
+
## Planetary Positions & Birth Charts
|
|
846
714
|
|
|
847
|
-
|
|
715
|
+
📖 [Birth Charts →](https://dharmagya.app/docs/panchang-ts/birth-chart)
|
|
848
716
|
|
|
849
717
|
```typescript
|
|
850
|
-
import {
|
|
718
|
+
import {
|
|
719
|
+
computePlanetaryPositions, computeLagna, computeBhava,
|
|
720
|
+
computeRashiChart, computeNavamsa, computeDivisionalChart, computeDignity,
|
|
721
|
+
} from 'panchang-ts';
|
|
851
722
|
|
|
852
723
|
const g = computePlanetaryPositions(new Date(), 'lahiri');
|
|
853
|
-
g.jupiter.rashi.name;
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
// True node (sharper Rahu/Ketu via Meeus periodic correction)
|
|
861
|
-
const gT = computePlanetaryPositions(new Date(), 'lahiri', undefined, 'true');
|
|
724
|
+
g.jupiter.rashi.name; g.jupiter.nakshatra.pada; g.saturn.isRetrograde;
|
|
725
|
+
|
|
726
|
+
const d1 = computeRashiChart(birth, loc); // houseSystem: whole-sign | equal | placidus-kp
|
|
727
|
+
d1.byPlanet.Mars.house; // keyed lookup, no linear scan
|
|
728
|
+
const d9 = computeNavamsa(birth, loc); // + D2/D3/D7/D10/D12/D30
|
|
729
|
+
computeDignity('Mars', 9); // 'exalted'
|
|
862
730
|
```
|
|
863
731
|
|
|
864
|
-
## Dashas
|
|
732
|
+
## Dashas & Personal Transits
|
|
865
733
|
|
|
866
|
-
|
|
734
|
+
📖 [Dashas & Transits →](https://dharmagya.app/docs/panchang-ts/dashas)
|
|
867
735
|
|
|
868
736
|
```typescript
|
|
869
737
|
import {
|
|
870
738
|
computeVimshottariDashaFromBirth, computeVimshottariPratyantar,
|
|
871
|
-
computeAshtottariDasha, computeYoginiDasha, computeCharaDasha,
|
|
739
|
+
computeAshtottariDasha, computeYoginiDasha, computeCharaDasha,
|
|
740
|
+
computeNarayanDasha, computeSadeSati,
|
|
872
741
|
} from 'panchang-ts';
|
|
873
742
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
// 2. Ashtottari — 108-year, 8-lord cycle (no Ketu).
|
|
879
|
-
const ash = computeAshtottariDasha(birth, moonLon);
|
|
880
|
-
|
|
881
|
-
// 3. Yogini — 36-year, 8 yoginis.
|
|
882
|
-
const yog = computeYoginiDasha(birth, moonLon);
|
|
883
|
-
yog.mahaDashas[0]!.yogini; // 'Dhanya'
|
|
884
|
-
yog.mahaDashas[0]!.lord; // 'Jupiter'
|
|
885
|
-
|
|
886
|
-
// 4. Chara (Jaimini) — sign-based, 9-8-7 years per modality, forward only.
|
|
887
|
-
const cha = computeCharaDasha(birth, loc);
|
|
888
|
-
|
|
889
|
-
// 5. Narayan (Jaimini) — sign-based, parity-based direction.
|
|
890
|
-
// Vishama-pada lagna {Aries, Taurus, Gemini, Libra, Scorpio, Sag} → forward
|
|
891
|
-
// Sama-pada lagna {Cancer, Leo, Virgo, Capricorn, Aquarius, Pisces} → backward
|
|
892
|
-
const nar = computeNarayanDasha(birth, loc);
|
|
893
|
-
nar.direction; // 'forward' | 'backward'
|
|
894
|
-
|
|
895
|
-
// Narayan variable-duration variant (Sanjay Rath):
|
|
896
|
-
const narV = computeNarayanDasha(birth, loc, 'lahiri', { duration: 'variable' });
|
|
897
|
-
narV.mahaDashas[0]!.years; // 0..12 from rashi-to-lord count (+1 exalt, -1 debil)
|
|
898
|
-
```
|
|
899
|
-
|
|
900
|
-
## Personal Transits
|
|
901
|
-
|
|
902
|
-
```typescript
|
|
903
|
-
const r = getDailyPanchang(date, loc, {
|
|
904
|
-
timezone: 330,
|
|
905
|
-
janmaRashi: 3, // 0 = Mesha … 11 = Meena
|
|
906
|
-
janmaNakshatra: 0, // 0 = Ashwini … 26 = Revati
|
|
907
|
-
})!;
|
|
908
|
-
r.chandraBalam; // { house, quality: 'strong'|'weak', name, englishName } — null without janmaRashi
|
|
909
|
-
r.tarabala; // { taraIndex, name, englishName, quality } — null without janmaNakshatra
|
|
910
|
-
|
|
911
|
-
import { computeSadeSati } from 'panchang-ts';
|
|
912
|
-
const ss = computeSadeSati(natalMoonRashiIndex, new Date());
|
|
913
|
-
// → { active, phase: 1|2|3|null, currentArcStart, currentArcEnd, nextArcStart }
|
|
743
|
+
const vim = computeVimshottariDashaFromBirth(birth, 'lahiri'); // 3-level
|
|
744
|
+
computeSadeSati(natalMoonRashiIndex, new Date());
|
|
745
|
+
// Daily transits: pass janmaRashi / janmaNakshatra to getDailyPanchang
|
|
746
|
+
// and read r.chandraBalam / r.tarabala.
|
|
914
747
|
```
|
|
915
748
|
|
|
916
|
-
##
|
|
749
|
+
## Strength, Yogas & Karakas
|
|
917
750
|
|
|
918
|
-
|
|
919
|
-
charts (D2/D3/D7/D9/D10/D12/D30), and Planetary Dignity.
|
|
751
|
+
📖 [Strength, Yogas & Karakas →](https://dharmagya.app/docs/panchang-ts/strength-yogas)
|
|
920
752
|
|
|
921
753
|
```typescript
|
|
922
754
|
import {
|
|
923
|
-
|
|
924
|
-
|
|
755
|
+
computeAspects, computeShadbala, computeBhavaBala,
|
|
756
|
+
computeAshtakavarga, computeYogas, computeJaiminiKarakas,
|
|
925
757
|
} from 'panchang-ts';
|
|
926
758
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
// Bhava — 'whole-sign' (default) | 'equal' | 'placidus-kp'.
|
|
933
|
-
// Placidus-KP throws PanchangError('CIRCUMPOLAR') beyond ±66.5°.
|
|
934
|
-
const houses = computeBhava(birth, loc, { houseSystem: 'whole-sign' });
|
|
935
|
-
|
|
936
|
-
// `chart.planets` is the ordered list; `chart.byPlanet` is the same nine
|
|
937
|
-
// placements keyed by graha, for direct lookup without a linear scan.
|
|
938
|
-
const chart = computeRashiChart(birth, loc);
|
|
939
|
-
chart.byPlanet.Mars.house; // instead of chart.planets.find(...)!
|
|
940
|
-
chart.planets.map(p => p.rashi); // iterate the list as before
|
|
941
|
-
|
|
942
|
-
// D1 — full Rashi chart with 9-graha house placement.
|
|
943
|
-
const d1 = computeRashiChart(birth, loc, { houseSystem: 'whole-sign' });
|
|
944
|
-
d1.planets.find(p => p.planet === 'Jupiter')?.house;
|
|
945
|
-
d1.planets.find(p => p.planet === 'Saturn')?.isRetrograde;
|
|
946
|
-
|
|
947
|
-
// Divisional charts (D2 Hora, D3 Drekkana, D7 Saptamsa, D9 Navamsa,
|
|
948
|
-
// D10 Dasamsa, D12 Dwadasamsa, D30 Trimsamsa).
|
|
949
|
-
const d9 = computeNavamsa(birth, loc);
|
|
950
|
-
const d10 = computeDivisionalChart(birth, loc, 'D10');
|
|
951
|
-
const d30 = computeDivisionalChart(birth, loc, 'D30');
|
|
952
|
-
|
|
953
|
-
// Planetary dignity (BPHS Ch.3-4).
|
|
954
|
-
computeDignity('Mars', 0); // 'moolatrikona' (Aries)
|
|
955
|
-
computeDignity('Mars', 9); // 'exalted' (Capricorn)
|
|
956
|
-
computeDignity('Sun', 6); // 'debilitated' (Libra)
|
|
759
|
+
computeShadbala(birth, loc); // 6-fold, in Virupas
|
|
760
|
+
computeAshtakavarga(d1, { reductions: true }); // Bhinna + Sarva + Sodhana
|
|
761
|
+
computeYogas(d1); // ~25 named, with bhanga
|
|
762
|
+
computeJaiminiKarakas(d1, { variant: '8-jaimini' });
|
|
957
763
|
```
|
|
958
764
|
|
|
959
|
-
Birth-chart helpers accept the full ayanamsa set including `'true-chitra'` and
|
|
960
|
-
`'thirukanitham'`.
|
|
961
|
-
|
|
962
765
|
## Compatibility & Doshas
|
|
963
766
|
|
|
767
|
+
📖 [Matching & Doshas →](https://dharmagya.app/docs/panchang-ts/matching-doshas)
|
|
768
|
+
|
|
964
769
|
```typescript
|
|
965
770
|
import {
|
|
966
771
|
computeAshtakoot, computePathuPorutham,
|
|
967
|
-
computeMangalDosha, computeKaalSarp, computePitruDosha,
|
|
772
|
+
computeMangalDosha, computeMangalCompatibility, computeKaalSarp, computePitruDosha,
|
|
968
773
|
} from 'panchang-ts';
|
|
969
774
|
|
|
970
|
-
|
|
971
|
-
// Graha Maitri, Gana, Bhakoot, Nadi (max 1/2/3/4/5/6/7/8).
|
|
972
|
-
const match = computeAshtakoot(
|
|
973
|
-
{ rashi: 4, nakshatra: 9 },
|
|
974
|
-
{ rashi: 0, nakshatra: 1 },
|
|
975
|
-
);
|
|
775
|
+
computeAshtakoot({ rashi: 4, nakshatra: 9 }, { rashi: 0, nakshatra: 1 });
|
|
976
776
|
// → { totalScore: 0..36, koots: KootScore[8], cancellations: string[] }
|
|
777
|
+
// Opt-in Gana-dosha cancellation (default off — preserves drik 36-guna parity):
|
|
778
|
+
computeAshtakoot(boy, girl, { ganaCancellation: true });
|
|
977
779
|
|
|
978
|
-
//
|
|
979
|
-
//
|
|
980
|
-
// `navamsaRashi` enables the same-Navamsa-lord rule.
|
|
981
|
-
const richer = computeAshtakoot(
|
|
982
|
-
{ rashi: 4, nakshatra: 9, lagnaRashi: 7, navamsaRashi: 2 },
|
|
983
|
-
{ rashi: 0, nakshatra: 1, lagnaRashi: 1, navamsaRashi: 5 },
|
|
984
|
-
);
|
|
985
|
-
|
|
986
|
-
// Manglik is a PAIRWISE verdict, not a per-chart one: when both partners are
|
|
987
|
-
// Manglik the two afflictions neutralise each other, so the pair is clean
|
|
988
|
-
// where a Manglik/non-Manglik pair is not.
|
|
989
|
-
const m = computeMangalCompatibility(boyChart, girlChart);
|
|
990
|
-
m.afflicted; // false when neither is Manglik AND when both are
|
|
991
|
-
m.cancellations; // ['both natives Manglik — mutual cancellation']
|
|
992
|
-
m.boy; m.girl; // each native's own MangalDoshaInfo, severity included
|
|
993
|
-
|
|
994
|
-
// Pathu Porutham (Tamil/Kerala, 10-fold) — binary pass/fail per koot.
|
|
995
|
-
// Three vetoes (Yoni, Rajju, Vedha) flip `recommended` regardless of count.
|
|
996
|
-
const tp = computePathuPorutham(
|
|
997
|
-
{ rashi: 4, nakshatra: 9 },
|
|
998
|
-
{ rashi: 0, nakshatra: 1 },
|
|
999
|
-
);
|
|
1000
|
-
tp.totalPasses; // 0..10
|
|
1001
|
-
tp.recommended; // no veto + ≥5 passes
|
|
1002
|
-
|
|
1003
|
-
// Doshas
|
|
1004
|
-
computeMangalDosha(d1);
|
|
1005
|
-
// Mars in 1/2/4/7/8/12 from Lagna, Moon, AND Venus (Drik rule set).
|
|
1006
|
-
// Cancellations: Mars in own sign/exalted, conjunct Jup/Moon/Venus,
|
|
1007
|
-
// or aspected by Jupiter (5/7/9 sign-aspect).
|
|
1008
|
-
// Severity (anshik/purna) is computed pre-cancellation.
|
|
1009
|
-
|
|
1010
|
-
computeKaalSarp(d1);
|
|
1011
|
-
// 12 subtypes by Rahu's house: anant, kulik, vasuki, shankhpal, padma,
|
|
1012
|
-
// mahapadma, takshak, karkotak, shankhachud, ghatak, vishdhar, sheshnag.
|
|
1013
|
-
|
|
1014
|
-
computePitruDosha(d1);
|
|
1015
|
-
// Pandit-consensus 4-trigger set (rules cited by ≥3 of 6 surveyed
|
|
1016
|
-
// pandit sources): Sun+Rahu conjunction (any house), Sun+Saturn
|
|
1017
|
-
// conjunction (any house), Rahu in 9th house, 9th-lord conjunct Rahu.
|
|
1018
|
-
// Drik panchang publishes no Pitru calculator; minority/expansive
|
|
1019
|
-
// rules (Sun in 9th alone, Ketu in 4th, 9th lord in dusthana, etc.)
|
|
1020
|
-
// are intentionally excluded.
|
|
1021
|
-
```
|
|
1022
|
-
|
|
1023
|
-
**Limitations.** Ashtakoot Vashya koot uses single-vashya per rashi.
|
|
1024
|
-
Bhakoot Parivartana (rashi-lord exchange) cancellation needs per-graha
|
|
1025
|
-
position data not carried by the `NatalMoon` shape and is not modelled.
|
|
1026
|
-
|
|
1027
|
-
## Strength & Aspects
|
|
1028
|
-
|
|
1029
|
-
```typescript
|
|
1030
|
-
import {
|
|
1031
|
-
computeAspects, computeShadbala, computeBhavaBala, computeAshtakavarga,
|
|
1032
|
-
} from 'panchang-ts';
|
|
1033
|
-
|
|
1034
|
-
// Drishti — every graha aspects the 7th; malefics gain extras
|
|
1035
|
-
// (Mars 4+8, Jupiter 5+9, Saturn 3+10). Node aspect mode is configurable:
|
|
1036
|
-
const aspects = computeAspects(d1); // BPHS 7th-only on nodes
|
|
1037
|
-
const aspExt = computeAspects(d1, { nodeAspects: '5-and-9' }); // KP/BV Raman extension
|
|
1038
|
-
|
|
1039
|
-
// Shadbala — 7 visible grahas, 6 components, in Virupas (60 V = 1 Rupa).
|
|
1040
|
-
// Sthana = Uchcha + Saptavargaja (D1/D2/D3/D7/D9/D12/D30 dignity sum)
|
|
1041
|
-
// + Ojha-Yugma (rashi+navamsa parity) + Drekkana (gender decanate).
|
|
1042
|
-
// Range [0, 420 V]. Dig is directional cusp; Kala = Nathonatha + Paksha;
|
|
1043
|
-
// Chesta is retrograde-bucket; Naisargika is fixed rank; Drik is weighted aspects.
|
|
1044
|
-
const bala = computeShadbala(birth, loc);
|
|
1045
|
-
|
|
1046
|
-
// Bhava Bala — 12-bhava strength built on top of Shadbala.
|
|
1047
|
-
// Per-bhava: { bhavadhipati, dik, drik, sthana, total }.
|
|
1048
|
-
const bhavaBala = computeBhavaBala(birth, loc);
|
|
1049
|
-
|
|
1050
|
-
// Ashtakavarga — 12-rashi bindu grids (BPHS Ch. 66).
|
|
1051
|
-
const av = computeAshtakavarga(d1);
|
|
1052
|
-
av.sarvashtaka; // 12 cells, each 0..56, total 336
|
|
1053
|
-
av.bhinnashtaka.Jupiter; // 12-cell grid; Jupiter total = 56 (chart-invariant)
|
|
1054
|
-
// Other invariants: Sun=47, Moon=49, Mars=39, Mercury=54, Venus=52, Saturn=39.
|
|
1055
|
-
|
|
1056
|
-
// Trikona + Ekadhipatya Sodhana reductions (BPHS Ch. 67):
|
|
1057
|
-
const avR = computeAshtakavarga(d1, { reductions: true });
|
|
1058
|
-
avR.reduced!.sarvashtaka;
|
|
1059
|
-
```
|
|
1060
|
-
|
|
1061
|
-
Rahu and Ketu are not Ashtakavarga receivers or contributors (classical
|
|
1062
|
-
Parashara scheme).
|
|
1063
|
-
|
|
1064
|
-
## Yogas & Karakas
|
|
1065
|
-
|
|
1066
|
-
```typescript
|
|
1067
|
-
import { computeYogas, computeJaiminiKarakas } from 'panchang-ts';
|
|
1068
|
-
|
|
1069
|
-
// ~25 named yogas — Pancha Mahapurusha (Ruchaka/Bhadra/Hamsa/Malavya/Sasha),
|
|
1070
|
-
// lunar (Gajakesari, Sunapha, Anapha, Durudhura, Kemadruma), solar
|
|
1071
|
-
// (Budha-Aditya, Veshi, Vasi, Ubhayachari), Raja (kendra/trikona-lord,
|
|
1072
|
-
// Dharma-Karmadhipati, Vipareeta, Lakshmi), Dhana (2-11, 5-9, Vasumati),
|
|
1073
|
-
// Vargottama, Yogakaraka, Neecha Bhanga, Daridra.
|
|
1074
|
-
const yogas = computeYogas(d1);
|
|
1075
|
-
// → [{ name, type, reasons[], bhanga?: { applies, reasons[] } }, …]
|
|
1076
|
-
|
|
1077
|
-
// Optional cancellation annotations: 5 Pancha Mahapurusha + Gajakesari
|
|
1078
|
-
// surface `bhanga` (Sun/Moon conjunct or Jupiter combust/debilitated).
|
|
1079
|
-
// Neecha Bhanga: dispositor in kendra from Lagna OR Moon; lord-of-
|
|
1080
|
-
// exaltation-rashi in kendra from Lagna or Moon; mutual exchange;
|
|
1081
|
-
// dispositor aspecting the debilitated planet.
|
|
1082
|
-
|
|
1083
|
-
// Filter by type / pass D9 for Vargottama:
|
|
1084
|
-
const d9 = computeNavamsa(birth, loc);
|
|
1085
|
-
const all = computeYogas(d1, { types: ['raja','dhana'], navamsa: d9 });
|
|
1086
|
-
|
|
1087
|
-
// Jaimini Karakas — Atmakaraka (highest degree-in-rashi) … Darakaraka (lowest).
|
|
1088
|
-
const k7 = computeJaiminiKarakas(d1); // 7-graha Parashara default
|
|
1089
|
-
const k8 = computeJaiminiKarakas(d1, { variant: '8-jaimini' }); // adds Rahu (degree reversed),
|
|
1090
|
-
// inserts Pitrukaraka at 5th
|
|
780
|
+
computeMangalCompatibility(boyChart, girlChart); // Manglik is a PAIRWISE verdict
|
|
781
|
+
computeKaalSarp(d1); // 12 subtypes by Rahu's house
|
|
1091
782
|
```
|
|
1092
783
|
|
|
1093
|
-
|
|
1094
|
-
**not** locale-resolved.
|
|
784
|
+
## Annual Charts, Sensitive Points, KP & Prashna
|
|
1095
785
|
|
|
1096
|
-
|
|
786
|
+
📖 [Annual Charts →](https://dharmagya.app/docs/panchang-ts/annual-charts) ·
|
|
787
|
+
[KP & Prashna →](https://dharmagya.app/docs/panchang-ts/kp-prashna)
|
|
1097
788
|
|
|
1098
789
|
```typescript
|
|
1099
790
|
import {
|
|
1100
791
|
computeVarshaphala, computeTithiPravesha, computeArudhas,
|
|
1101
792
|
computeHoraLagna, computeGhatiLagna, computeBhavaLagna, computeSripatiLagna,
|
|
1102
793
|
computeUpagrahas, computeArgala,
|
|
1103
|
-
} from 'panchang-ts';
|
|
1104
|
-
|
|
1105
|
-
// Varshaphala — Tajik annual chart for the Nth solar return.
|
|
1106
|
-
const v = computeVarshaphala(birth, 30, loc);
|
|
1107
|
-
v.solarReturnInstant;
|
|
1108
|
-
v.varshaLagna.rashi.name;
|
|
1109
|
-
v.muntha.rashi; v.muntha.house; // muntha = (natalLagnaRashi + 30) mod 12
|
|
1110
|
-
v.yearLord; // strongest of 4 candidates by Shadbala
|
|
1111
|
-
v.sahams.Punya.house;
|
|
1112
|
-
v.sahams.Vivaha.rashi;
|
|
1113
|
-
// 27 Sahams: Punya, Vidya, Yasas, Mitra, Karma, Vivaha, Putra, Roga, Marana,
|
|
1114
|
-
// Rajya, Raja, Bandhu, Dharma, Gnati, Apamrityu, Bhratri, Matri, Pitri, Sama,
|
|
1115
|
-
// Bandhana, Karyasiddhi, Vyapara, Sastra, Asha, Labha, Susha, Tapas.
|
|
1116
|
-
|
|
1117
|
-
// Tithi Pravesha — annual chart cast when Sun is in natal sidereal sign AND
|
|
1118
|
-
// Sun-Moon separation equals natal separation. Preserves natal tithi exactly.
|
|
1119
|
-
const tp = computeTithiPravesha(birth, 30, loc);
|
|
1120
|
-
tp.natalTithi === tp.praveshTithi; // always true
|
|
1121
|
-
|
|
1122
|
-
// Arudha padas — image/reflection of each bhava. Arudha[0] = Arudha Lagna (AL).
|
|
1123
|
-
const a = computeArudhas(d1);
|
|
1124
|
-
a[0]!.bhava; // 1 — AL
|
|
1125
|
-
a[0]!.arudhaRashi; // 0..11
|
|
1126
|
-
a[6]!.bhava; // 7 — Darapada (spouse pada)
|
|
1127
|
-
|
|
1128
|
-
// Special lagnas — time-derived sensitive points from sunrise on/before birth.
|
|
1129
|
-
computeHoraLagna(birth, loc); // 30°/hour (1 rashi/hour)
|
|
1130
|
-
computeGhatiLagna(birth, loc); // 75°/hour (1 rashi/24 min)
|
|
1131
|
-
computeBhavaLagna(birth, loc); // 15°/hour (1 rashi/2 hours)
|
|
1132
|
-
computeSripatiLagna(birth, loc); // = natal lagna (cusp 1)
|
|
1133
|
-
|
|
1134
|
-
// Sripati cusps 2–12 (opt-in) — 4 angular cusps + trisected intermediates.
|
|
1135
|
-
// Defined at every latitude (unlike Placidus).
|
|
1136
|
-
const sripati = computeSripatiLagna(birth, loc, 'lahiri', 'en', { includeCusps: true });
|
|
1137
|
-
sripati.cusps; // number[12] of bhava madhyas; cusps[0/3/6/9] = ASC/IC/DSC/MC
|
|
1138
|
-
|
|
1139
|
-
// Upagrahas — Gulika, Mandi (rising-asc at Saturn segment start/midpoint),
|
|
1140
|
-
// plus Sun-derived Dhuma, Vyatipata, Parivesha, Indrachapa, Upaketu.
|
|
1141
|
-
const u = computeUpagrahas(birth, loc);
|
|
1142
|
-
u.gulika.longitude; u.gulika.rashi; u.gulika.house;
|
|
1143
|
-
|
|
1144
|
-
// Argala (Jaimini) — planets in 2/4/11 from a bhava form Argala (intervention);
|
|
1145
|
-
// 3/10/12 form Virodhargala (counter). Each planet hits exactly 6 of 12 bhavas.
|
|
1146
|
-
const arg = computeArgala(d1);
|
|
1147
|
-
arg[0]!.argala; arg[0]!.virodhargala;
|
|
1148
|
-
|
|
1149
|
-
// Trikonargala (5/9 trine, opt-in) — Ketu reversal: 5th-from → virodhaka,
|
|
1150
|
-
// 9th-from → source.
|
|
1151
|
-
const argT = computeArgala(d1, { includeTrikonargala: true });
|
|
1152
|
-
argT[0]!.trikona!.sources;
|
|
1153
|
-
argT[0]!.trikona!.virodhakas;
|
|
1154
|
-
```
|
|
1155
|
-
|
|
1156
|
-
## KP & Prashna
|
|
1157
|
-
|
|
1158
|
-
```typescript
|
|
1159
|
-
import {
|
|
1160
794
|
computeKpSubLord, computeKpCuspalSubLords, computeKpSignificators,
|
|
1161
795
|
computePrashnaChart,
|
|
1162
796
|
} from 'panchang-ts';
|
|
1163
797
|
|
|
1164
|
-
|
|
1165
|
-
//
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
info.starLord; // 'Moon'
|
|
1169
|
-
info.subLord;
|
|
1170
|
-
|
|
1171
|
-
// Cuspal sub-lords (always Placidus-KP — KP's anchor scheme).
|
|
1172
|
-
const cusps = computeKpCuspalSubLords(birth, loc);
|
|
1173
|
-
cusps.cusps[0]!.subLord; // ascendant
|
|
1174
|
-
cusps.cusps[6]!.subLord; // descendant
|
|
1175
|
-
|
|
1176
|
-
// Significators — for each planet, the houses it signifies via the 4-fold KP rule
|
|
1177
|
-
// (occupant + star-lord-occupant + owner + star-lord-owner).
|
|
1178
|
-
const sig = computeKpSignificators(d1);
|
|
1179
|
-
sig.byPlanet.Sun;
|
|
1180
|
-
sig.byHouse[10];
|
|
1181
|
-
|
|
1182
|
-
// Prashna (horary) chart — cast at question moment from querent's location.
|
|
1183
|
-
const pchart = computePrashnaChart(
|
|
1184
|
-
new Date('2026-05-09T14:30:00Z'),
|
|
1185
|
-
{ latitude: 19.0760, longitude: 72.8777 },
|
|
1186
|
-
);
|
|
1187
|
-
pchart.lagna.rashi.name;
|
|
1188
|
-
pchart.bhava.system; // 'placidus-kp' by default (KP horary anchor)
|
|
1189
|
-
pchart.planets[1]!.house; // Moon — primary mind significator
|
|
1190
|
-
```
|
|
1191
|
-
|
|
1192
|
-
Same return shape as a natal `BirthChart`. Pass `{ houseSystem: 'whole-sign' }`
|
|
1193
|
-
to `computePrashnaChart` for traditional Vedic Prashna.
|
|
1194
|
-
|
|
1195
|
-
## Muhurta Engine
|
|
1196
|
-
|
|
1197
|
-
```typescript
|
|
1198
|
-
import { scoreMuhurta, computeAuspiciousDatesInRange, vivahRule } from 'panchang-ts';
|
|
1199
|
-
|
|
1200
|
-
const r = scoreMuhurta(new Date('2026-05-12'), DELHI, vivahRule, { timezone: 330 });
|
|
1201
|
-
// → { date, score: 0..100, passes: boolean,
|
|
1202
|
-
// reasons: string[], // diagnostic English
|
|
1203
|
-
// factors: MuhurtaFactor[] } // { code, axis, index?, delta } — stable
|
|
1204
|
-
|
|
1205
|
-
r.factors.filter(f => f.delta < 0); // what cost the day points
|
|
1206
|
-
r.factors.some(f => f.axis === 'exclusion'); // hard-excluded?
|
|
1207
|
-
|
|
1208
|
-
const dates = computeAuspiciousDatesInRange(
|
|
1209
|
-
vivahRule,
|
|
1210
|
-
new Date('2026-05-01'),
|
|
1211
|
-
new Date('2026-05-31'),
|
|
1212
|
-
DELHI,
|
|
1213
|
-
{ timezone: 330 },
|
|
1214
|
-
); // MuhurtaDay[] sorted by score desc; full panchang attached
|
|
1215
|
-
|
|
1216
|
-
// Custom rule (pure data, no engine code needed)
|
|
1217
|
-
const myRule: MuhurtaRule = {
|
|
1218
|
-
occasion: 'launch_party',
|
|
1219
|
-
auspiciousVaras: [3, 4, 5],
|
|
1220
|
-
auspiciousNakshatras: [11, 12, 21],
|
|
1221
|
-
bhadra: 'penalize', // 'ignore' | 'penalize' | 'exclude'
|
|
1222
|
-
excludeEkadashi: true,
|
|
1223
|
-
excludeAdhikaMasa: true,
|
|
1224
|
-
};
|
|
798
|
+
const v = computeVarshaphala(birth, 30, loc); // Tajik + Muntha + 27 Sahams
|
|
799
|
+
computeTithiPravesha(birth, 30, loc); // preserves natal tithi exactly
|
|
800
|
+
computeKpSubLord(45.5).subLord; // 243 sub-divisions
|
|
801
|
+
computePrashnaChart(questionTime, querentLoc); // horary, Placidus-KP default
|
|
1225
802
|
```
|
|
1226
803
|
|
|
1227
|
-
**Tithi and vara are scored jointly, not just per-anga.** The classical Vara ×
|
|
1228
|
-
Tithi yogas — Siddha, Amrita, Dagdha, Visha, Hutasana, Krakacha, Samvartaka —
|
|
1229
|
-
are applied to every rule, so a Rikta tithi landing on a Saturday is partly
|
|
1230
|
-
redeemed by Siddha yoga rather than flatly penalised. Set
|
|
1231
|
-
`varaTithiYogas: false` for the older per-anga-only scoring, or call
|
|
1232
|
-
`computeVaraTithiYogas(vara, tithi)` directly. Where an auspicious and an
|
|
1233
|
-
inauspicious yoga both fire — a documented ambiguity in the sources — both are
|
|
1234
|
-
surfaced as separate factors and allowed to net out.
|
|
1235
|
-
|
|
1236
|
-
`bhadra` defaults to `'ignore'`; the stock rules use `'penalize'`. A whole-day
|
|
1237
|
-
`'exclude'` is rarely what you want: Vishti karana sits at fixed positions in
|
|
1238
|
-
the tithi cycle, so vetoing the day removes seven tithis outright — among them
|
|
1239
|
-
Shukla Ekadashi, which the same sources list as *preferred* for vivah. Read
|
|
1240
|
-
`panchang.inauspicious.bhadra` for the window and schedule around it.
|
|
1241
|
-
`excludeBhadra: true` still works as an alias for `bhadra: 'exclude'`.
|
|
1242
|
-
|
|
1243
|
-
13 stock rules: vivah, griha pravesh, namakarana, vidyarambh, vahan kharidi,
|
|
1244
|
-
annaprashan, mundan, upanayanam, karnavedha, aksharabhyasam, seemantham, shop
|
|
1245
|
-
opening, travel start.
|
|
1246
|
-
|
|
1247
|
-
### Pre-computed table — build your own and cache it
|
|
1248
|
-
|
|
1249
|
-
Scoring a year of days runs the engine ~365 times. If your app asks the same
|
|
1250
|
-
question repeatedly, compute the answer once and ship the JSON — the same
|
|
1251
|
-
pattern the festival, eclipse and Moon-phase tables use.
|
|
1252
|
-
|
|
1253
|
-
```typescript
|
|
1254
|
-
import { buildMuhurtaTable, vivahRule } from 'panchang-ts';
|
|
1255
|
-
|
|
1256
|
-
const table = buildMuhurtaTable({
|
|
1257
|
-
rule: vivahRule,
|
|
1258
|
-
location: { latitude: 25.3176, longitude: 82.9739 },
|
|
1259
|
-
timezoneOffsetMinutes: 330,
|
|
1260
|
-
startYear: 2026,
|
|
1261
|
-
endYear: 2031,
|
|
1262
|
-
referenceLocation: 'Varanasi',
|
|
1263
|
-
});
|
|
1264
|
-
// persist JSON.stringify(table) — 6 years of vivah dates is ~74 KB
|
|
1265
|
-
```
|
|
1266
|
-
|
|
1267
|
-
Read it back through the engine-free `panchang-ts/muhurta` entry (~1.7 KB, no
|
|
1268
|
-
astronomy code in your bundle):
|
|
1269
|
-
|
|
1270
|
-
```typescript
|
|
1271
|
-
import {
|
|
1272
|
-
readMuhurtaForYear,
|
|
1273
|
-
readMuhurtaForDate,
|
|
1274
|
-
readMuhurtaYearRange,
|
|
1275
|
-
readMuhurtaOccasion,
|
|
1276
|
-
readBestMuhurtaDays,
|
|
1277
|
-
} from 'panchang-ts/muhurta';
|
|
1278
|
-
|
|
1279
|
-
const table = JSON.parse(await (await fetch('/muhurta-vivah.json')).text());
|
|
1280
|
-
|
|
1281
|
-
readMuhurtaOccasion(table); // 'vivah'
|
|
1282
|
-
readMuhurtaYearRange(table); // { start: 2026, end: 2031 }
|
|
1283
|
-
readMuhurtaForYear(table, 2026); // MuhurtaTableDay[] — passing days, by date
|
|
1284
|
-
readMuhurtaForDate(table, '2026-11-11');
|
|
1285
|
-
readBestMuhurtaDays(table, 5); // top 5 across the table, highest first
|
|
1286
|
-
```
|
|
1287
|
-
|
|
1288
|
-
Only days that **pass** the rule are stored by default; pass
|
|
1289
|
-
`includeFailures: true` to keep every day with its score. Scores are location-
|
|
1290
|
-
*and* rule-dependent, so a table built for Varanasi and `vivah` says nothing
|
|
1291
|
-
about another place or occasion.
|
|
1292
|
-
|
|
1293
|
-
`npm run muhurta:gen` is a worked example script
|
|
1294
|
-
([scripts/generate-muhurta-json.ts](scripts/generate-muhurta-json.ts)):
|
|
1295
|
-
|
|
1296
|
-
```bash
|
|
1297
|
-
npm run muhurta:gen -- muhurta-vivah.json vivah
|
|
1298
|
-
```
|
|
1299
|
-
|
|
1300
|
-
Scoring: starts at 50; +10 per matching auspicious axis (tithi / nakshatra /
|
|
1301
|
-
vara / yoga), -15 per inauspicious axis, hard exclusions zero the score.
|
|
1302
|
-
Special yogas (Amrit Siddhi, Sarvartha Siddhi, Ravi/Guru Pushya) add +5;
|
|
1303
|
-
Jwalamukhi subtracts -10. Clamped 0..100; `passes: true` when score ≥ 50.
|
|
1304
|
-
|
|
1305
|
-
Every scoring input appears in both `reasons` (English prose, diagnostic, not a
|
|
1306
|
-
stable format) and `factors` (structured, with a stable `code`). Localize and
|
|
1307
|
-
filter on `factors`.
|
|
1308
|
-
|
|
1309
|
-
`scoreMuhurta` computes only the sections it actually scores against, so it is
|
|
1310
|
-
cheaper than a full `getDailyPanchang`. `computeAuspiciousDatesInRange` does not narrow —
|
|
1311
|
-
each returned day carries its complete `panchang` for callers to drill into.
|
|
1312
|
-
|
|
1313
804
|
## Calendar Conversion
|
|
1314
805
|
|
|
806
|
+
📖 [Calendar Conversion →](https://dharmagya.app/docs/panchang-ts/calendar-conversion)
|
|
807
|
+
|
|
1315
808
|
```typescript
|
|
1316
809
|
import {
|
|
1317
810
|
convertGregorianToHindu, convertHinduToGregorian,
|
|
1318
811
|
getKaliYugaYear, getHinduNewYear,
|
|
1319
812
|
computeEkadashiDatesForYear, computeSankrantisForYear,
|
|
1320
|
-
computeFestivalsInRange, getUpcomingEclipses, computeEclipsesInRange,
|
|
1321
813
|
} from 'panchang-ts';
|
|
1322
814
|
|
|
1323
|
-
|
|
1324
|
-
const h = convertGregorianToHindu(new Date('2026-04-15'), DELHI, { timezone: 330 });
|
|
1325
|
-
|
|
1326
|
-
// Hindu → Gregorian
|
|
1327
|
-
const dates = convertHinduToGregorian(
|
|
815
|
+
convertHinduToGregorian(
|
|
1328
816
|
{ vikramSamvat: 2083, masaIndex: 0, paksha: 'shukla', pakshaTithi: 9 },
|
|
1329
817
|
DELHI, { timezone: 330 },
|
|
1330
|
-
);
|
|
1331
|
-
|
|
1332
|
-
getKaliYugaYear(new Date('2026-04-01')); // 5127
|
|
1333
|
-
getHinduNewYear(2026, 'tamil-nadu', DELHI, { timezone: 330 }); // Puthandu
|
|
1334
|
-
|
|
1335
|
-
computeEkadashiDatesForYear(2026, DELHI, { timezone: 330 }); // ~24 Date[]
|
|
1336
|
-
computeSankrantisForYear(2026, DELHI, { timezone: 330 }); // 12 SankrantiEvent[]
|
|
1337
|
-
computeFestivalsInRange(start, end, DELHI, { timezone: 330 }); // FestivalDay[]
|
|
1338
|
-
getUpcomingEclipses(new Date(), DELHI, 5);
|
|
818
|
+
); // → Date[] (Rama Navami VS 2083)
|
|
819
|
+
getHinduNewYear(2026, 'tamil-nadu', DELHI, { timezone: 330 }); // region-aware
|
|
1339
820
|
```
|
|
1340
821
|
|
|
1341
|
-
`getHinduNewYear` is region-aware: Tamil Nadu / Kerala / Punjab / Bengal / Assam
|
|
1342
|
-
use the **solar** (Mesha Sankranti) anchor; elsewhere uses **Chaitra Shukla
|
|
1343
|
-
Pratipada** (Ugadi / Gudi Padwa / Cheti Chand). When Pratipada is a kshaya
|
|
1344
|
-
tithi (e.g. Ugadi 2026), falls back to the Amanta-Chaitra-masa boundary.
|
|
1345
|
-
|
|
1346
822
|
## Localization & Configuration
|
|
1347
823
|
|
|
1348
|
-
|
|
1349
|
-
const hi = getDailyPanchang(date, loc, { timezone: 330, language: 'hi' })!;
|
|
1350
|
-
hi.angas.tithis[0].name; // "कृष्ण चतुर्दशी"
|
|
1351
|
-
hi.angas.vara.name; // "मंगलवार"
|
|
1352
|
-
hi.calendar.chandramasa.name; // "माघ"
|
|
1353
|
-
hi.periods.choghadiya.day[0].name; // "अमृत"
|
|
1354
|
-
hi.angas.vara.englishName; // "Tuesday" — englishName always English
|
|
1355
|
-
|
|
1356
|
-
// All options:
|
|
1357
|
-
const r = getDailyPanchang(date, loc, {
|
|
1358
|
-
timezone: 330, // number (UTC offset min) or IANA string
|
|
1359
|
-
ayanamsa: 'lahiri', // lahiri | raman | krishnamurti | true-chitra | thirukanitham
|
|
1360
|
-
language: 'en', // en | hi
|
|
1361
|
-
masaSystem: 'purnimanta', // purnimanta | amanta
|
|
1362
|
-
region: 'all', // 21 state slugs + 'nepal' + 'all'
|
|
1363
|
-
computeEndTimes: true, // false → skip transition searches
|
|
1364
|
-
sections: undefined, // undefined = all; see Performance
|
|
1365
|
-
janmaRashi: undefined, // pass to populate r.chandraBalam (else null)
|
|
1366
|
-
janmaNakshatra: undefined, // pass to populate r.tarabala (else null)
|
|
1367
|
-
});
|
|
1368
|
-
```
|
|
1369
|
-
|
|
1370
|
-
**Timezone.** Number (minutes from UTC, e.g. `330` for IST) or an IANA zone
|
|
1371
|
-
name (e.g. `'America/New_York'`). IANA strings need `Intl`, which older Hermes
|
|
1372
|
-
versions lack — pass a number on those targets. DST resolves automatically for
|
|
1373
|
-
IANA zones.
|
|
1374
|
-
|
|
1375
|
-
---
|
|
1376
|
-
|
|
1377
|
-
### Localized vs machine-readable fields
|
|
1378
|
-
|
|
1379
|
-
Every user-facing string follows `language`. Where a value is also meaningful to
|
|
1380
|
-
code, the two are separate fields — the stable key never changes with language:
|
|
1381
|
-
|
|
1382
|
-
| Machine-readable | Localized display |
|
|
1383
|
-
|---|---|
|
|
1384
|
-
| `festival.key` (`'diwali'`) | `festival.name` (`"दिवाली"`) |
|
|
1385
|
-
| `bhadra.location` (`'paatal'`) | `bhadra.locationName` (`"पाताल"`) |
|
|
1386
|
-
| `eclipse.kind` / `eclipse.subtype` | `eclipse.description` |
|
|
1387
|
-
| `muhurtaScore.factors[].code` | `muhurtaScore.reasons` (English only) |
|
|
1388
|
-
|
|
1389
|
-
`MuhurtaScore.reasons` is diagnostic English and not a stable format; use
|
|
1390
|
-
`factors` for anything shown to a user or branched on in code.
|
|
1391
|
-
|
|
1392
|
-
## Types & Exports
|
|
1393
|
-
|
|
1394
|
-
<details>
|
|
1395
|
-
<summary><strong>Core, Pancha Anga, Festivals</strong></summary>
|
|
824
|
+
📖 [Options & Localization →](https://dharmagya.app/docs/panchang-ts/localization)
|
|
1396
825
|
|
|
1397
826
|
```typescript
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
interface TithiInfo {
|
|
1402
|
-
index: number; // 0-29
|
|
1403
|
-
name: string;
|
|
1404
|
-
paksha: string; // "Shukla"/"Krishna" (en), "शुक्ल"/"कृष्ण" (hi)
|
|
1405
|
-
number: number; // 1-15 within the paksha
|
|
1406
|
-
completionPercentage: number;
|
|
1407
|
-
endTime: Date | null;
|
|
1408
|
-
}
|
|
1409
|
-
// NakshatraInfo, YogaInfo, KaranaInfo follow the same pattern.
|
|
1410
|
-
// DailyTithiInfo extends with startTime + isActiveAtSunrise.
|
|
1411
|
-
|
|
1412
|
-
interface VaraInfo {
|
|
1413
|
-
index: number; // 0 = Sunday … 6 = Saturday
|
|
1414
|
-
name: string; // localized (e.g. "Raviwara")
|
|
1415
|
-
shortName: string;
|
|
1416
|
-
englishName: string; // always English
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
|
-
interface FestivalInfo {
|
|
1420
|
-
key: string; // stable, language-independent id — match on this
|
|
1421
|
-
name: string; // localized — display only
|
|
1422
|
-
type: 'major' | 'minor' | 'ekadashi' | 'smarta_ekadashi' | 'vaishnava_ekadashi'
|
|
1423
|
-
| 'pradosha' | 'sankranti' | 'eclipse';
|
|
1424
|
-
description?: string;
|
|
1425
|
-
deferralDate?: Date; // Smarta Ekadashi → Dwadashi fast date
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
type FestivalRegion =
|
|
1429
|
-
| 'all'
|
|
1430
|
-
| 'tamil-nadu' | 'kerala' | 'karnataka' | 'andhra-pradesh' | 'telangana'
|
|
1431
|
-
| 'west-bengal' | 'odisha' | 'assam' | 'bihar' | 'jharkhand'
|
|
1432
|
-
| 'gujarat' | 'maharashtra' | 'goa' | 'rajasthan'
|
|
1433
|
-
| 'punjab' | 'haryana' | 'himachal-pradesh' | 'uttarakhand'
|
|
1434
|
-
| 'uttar-pradesh' | 'madhya-pradesh'
|
|
1435
|
-
| 'nepal';
|
|
1436
|
-
|
|
1437
|
-
// Legacy slugs accepted (mapped internally): 'tamil' → 'tamil-nadu',
|
|
1438
|
-
// 'bengal' → 'west-bengal', 'north-india' → 'all'.
|
|
1439
|
-
```
|
|
1440
|
-
|
|
1441
|
-
</details>
|
|
1442
|
-
|
|
1443
|
-
<details>
|
|
1444
|
-
<summary><strong>Eclipses & Bhadra</strong></summary>
|
|
1445
|
-
|
|
1446
|
-
```typescript
|
|
1447
|
-
interface EclipseInfo {
|
|
1448
|
-
kind: 'solar' | 'lunar';
|
|
1449
|
-
subtype: 'partial' | 'total' | 'annular' | 'penumbral';
|
|
1450
|
-
start: Date; peak: Date; end: Date;
|
|
1451
|
-
visibleFromLocation: boolean;
|
|
1452
|
-
obscuration: number; // disc AREA covered at peak, [0, 1]
|
|
1453
|
-
magnitude: number; // catalogue magnitude — disc DIAMETER covered.
|
|
1454
|
-
// Not [0, 1]: >1 for a total eclipse, negative
|
|
1455
|
-
// for a penumbral lunar one (the Moon misses
|
|
1456
|
-
// the umbra), exactly as NASA's canon prints it.
|
|
1457
|
-
sutakStart: Date; // 12 h pre-solar / 9 h pre-lunar
|
|
1458
|
-
sutakEnd: Date;
|
|
1459
|
-
description: string;
|
|
1460
|
-
}
|
|
1461
|
-
|
|
1462
|
-
interface BhadraInfo {
|
|
1463
|
-
start: Date; end: Date;
|
|
1464
|
-
location: 'earth' | 'heaven' | 'paatal'; // 'earth' = malefic for all work
|
|
1465
|
-
locationName: string; // localized display name
|
|
1466
|
-
isActive: boolean;
|
|
1467
|
-
}
|
|
1468
|
-
```
|
|
1469
|
-
|
|
1470
|
-
</details>
|
|
1471
|
-
|
|
1472
|
-
<details>
|
|
1473
|
-
<summary><strong>Jyotish</strong></summary>
|
|
1474
|
-
|
|
1475
|
-
```typescript
|
|
1476
|
-
type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter'
|
|
1477
|
-
| 'Venus' | 'Saturn' | 'Rahu' | 'Ketu';
|
|
1478
|
-
|
|
1479
|
-
interface GrahaPosition {
|
|
1480
|
-
planet: GrahaName;
|
|
1481
|
-
siderealLongitude: number;
|
|
1482
|
-
rashi: RashiInfo;
|
|
1483
|
-
degreeInRashi: number;
|
|
1484
|
-
nakshatra: NakshatraInfo;
|
|
1485
|
-
isRetrograde: boolean; // always false for Sun/Moon; always true for Rahu/Ketu
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars'
|
|
1489
|
-
| 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
|
|
1490
|
-
|
|
1491
|
-
interface MahaDasha { lord: DashaLord; startDate: Date; endDate: Date;
|
|
1492
|
-
years: number; antarDashas: AntarDasha[]; }
|
|
1493
|
-
interface VimshottariDashaResult {
|
|
1494
|
-
currentMahaDashaLord: DashaLord;
|
|
1495
|
-
currentIndex: number;
|
|
1496
|
-
mahaDashas: MahaDasha[];
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
interface ChandraBalamInfo {
|
|
1500
|
-
house: number; // 1 = janma rashi; 12 = rashi before janma
|
|
1501
|
-
quality: 'strong' | 'weak'; // Shubha houses = 1,3,6,7,10,11
|
|
1502
|
-
englishName: string; // "Shubha" | "Ashubha"
|
|
1503
|
-
name: string;
|
|
1504
|
-
}
|
|
827
|
+
const hi = getDailyPanchang(date, loc, { timezone: 330, language: 'hi' })!;
|
|
828
|
+
hi.angas.tithis[0].name; // "कृष्ण चतुर्दशी"
|
|
829
|
+
hi.angas.vara.englishName; // "Tuesday" — englishName always English
|
|
1505
830
|
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
// | Sadhaka | Vadha | Mitra | Ati-Mitra
|
|
1510
|
-
name: string;
|
|
1511
|
-
quality: 'auspicious' | 'inauspicious';
|
|
1512
|
-
}
|
|
831
|
+
// All options: timezone (number | IANA string), ayanamsa (5), language (en|hi),
|
|
832
|
+
// masaSystem (purnimanta|amanta), region, computeEndTimes, sections,
|
|
833
|
+
// janmaRashi, janmaNakshatra.
|
|
1513
834
|
```
|
|
1514
835
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
<summary><strong>Full export list</strong></summary>
|
|
836
|
+
Machine-readable keys never change with language: match on `festival.key`,
|
|
837
|
+
`bhadra.location`, `eclipse.kind`, `factors[].code` — render `name` /
|
|
838
|
+
`locationName` / `description` / `reasons`.
|
|
1519
839
|
|
|
1520
|
-
|
|
1521
|
-
// Primary entry points
|
|
1522
|
-
getDailyPanchang, getInstantPanchang
|
|
1523
|
-
|
|
1524
|
-
// Astronomy
|
|
1525
|
-
getSunrise, getSunset, getMoonrise, getMoonset
|
|
1526
|
-
getSiderealSunLongitude, getSiderealMoonLongitude, getAyanamsa
|
|
1527
|
-
|
|
1528
|
-
// Inauspicious / Muhurta
|
|
1529
|
-
computeRahuKalam, computeGulikaKalam, computeYamaganda
|
|
1530
|
-
computeVarjyam, computeGandaMula, computeAnandadiYoga
|
|
1531
|
-
computePanchakaRahita, computeDoGhati, computeGowriPanchangam
|
|
1532
|
-
computePanchaka, classifyPanchaka, isPanchakaDosha, findPanchakaOnset
|
|
1533
|
-
computeAbhijitMuhurta, computeBrahmaMuhurta, computeVijayaMuhurta
|
|
1534
|
-
computeGodhuliMuhurta, computeNishitaMuhurta, computeAmritKala
|
|
1535
|
-
computeMadhyahna, computePratahSandhya, computeSayahnaSandhya
|
|
1536
|
-
|
|
1537
|
-
// Eclipses
|
|
1538
|
-
getUpcomingSolarEclipse, getUpcomingLunarEclipse, getEclipseDuringDay
|
|
1539
|
-
isEclipseVisibleAnyPhase
|
|
1540
|
-
|
|
1541
|
-
// Moon phases (new / quarters / full as precise instants)
|
|
1542
|
-
computeMoonPhasesInRange
|
|
1543
|
-
|
|
1544
|
-
// Jyotish — planets, dashas, transits
|
|
1545
|
-
computePlanetaryPositions, GRAHA_ABBR
|
|
1546
|
-
computeVimshottariDasha, computeVimshottariDashaFromBirth, computeVimshottariPratyantar
|
|
1547
|
-
computeAshtottariDasha, computeYoginiDasha, computeCharaDasha, computeNarayanDasha
|
|
1548
|
-
computeChandraBalam, computeTarabala, computeSadeSati
|
|
1549
|
-
|
|
1550
|
-
// Jyotish — chart
|
|
1551
|
-
computeLagna, computeBhava, computeRashiChart, computeNavamsa, computeDivisionalChart
|
|
1552
|
-
computeHoraLagna, computeGhatiLagna, computeBhavaLagna, computeSripatiLagna
|
|
1553
|
-
computeDignity
|
|
1554
|
-
|
|
1555
|
-
// Jyotish — strength, yogas, sensitive
|
|
1556
|
-
computeAspects, computeShadbala, computeBhavaBala, computeAshtakavarga
|
|
1557
|
-
computeYogas, computeJaiminiKarakas
|
|
1558
|
-
computeVarshaphala, computeTithiPravesha, computeArudhas, computeUpagrahas, computeArgala
|
|
1559
|
-
|
|
1560
|
-
// Jyotish — compatibility, doshas
|
|
1561
|
-
computeAshtakoot, computePathuPorutham
|
|
1562
|
-
computeMangalDosha, computeMangalCompatibility, computeKaalSarp, computePitruDosha
|
|
1563
|
-
|
|
1564
|
-
// KP / Prashna
|
|
1565
|
-
computeKpSubLord, computeKpCuspalSubLords, computeKpSignificators
|
|
1566
|
-
computePrashnaChart
|
|
1567
|
-
|
|
1568
|
-
// Muhurta engine
|
|
1569
|
-
scoreMuhurta, computeAuspiciousDatesInRange, STOCK_MUHURTA_RULES
|
|
1570
|
-
computeVaraTithiYogas
|
|
1571
|
-
vivahRule, grihaPraveshRule, namakaranaRule, vidyarambhRule, vahanKharidiRule
|
|
1572
|
-
annaprashanRule, mundanRule, upanayanamRule, karnavedhaRule
|
|
1573
|
-
aksharabhyasamRule, seemanthamRule, shopOpeningRule, travelStartRule
|
|
1574
|
-
|
|
1575
|
-
// Calendar conversion + yearly listings
|
|
1576
|
-
convertGregorianToHindu, convertHinduToGregorian
|
|
1577
|
-
getKaliYugaYear, getHinduNewYear, computeSamvat
|
|
1578
|
-
computeEkadashiDatesForYear, computeSankrantisForYear, computeFestivalsInRange
|
|
1579
|
-
getUpcomingEclipses, computeEclipsesInRange
|
|
1580
|
-
|
|
1581
|
-
// Static data tables — build one, cache the JSON, then read it back through
|
|
1582
|
-
// the engine-free panchang-ts/festivals · /eclipses · /moon-phases entries.
|
|
1583
|
-
// No table ships with the package.
|
|
1584
|
-
buildFestivalsTable, buildEclipsesTable, buildMoonPhasesTable
|
|
1585
|
-
|
|
1586
|
-
// Errors
|
|
1587
|
-
PanchangError
|
|
1588
|
-
```
|
|
840
|
+
## Types & Exports
|
|
1589
841
|
|
|
1590
|
-
|
|
842
|
+
📖 [Types & Exports →](https://dharmagya.app/docs/panchang-ts/types) — the key
|
|
843
|
+
interfaces (`TithiInfo`, `FestivalInfo`, `EclipseInfo`, `GrahaPosition`, …) and
|
|
844
|
+
the complete export list of the main entry and the four engine-free subpaths
|
|
845
|
+
(`panchang-ts/festivals`, `/eclipses`, `/moon-phases`, `/muhurta`).
|
|
1591
846
|
|
|
1592
847
|
---
|
|
1593
848
|
|
|
@@ -1602,10 +857,7 @@ Two-pass rendering pattern for smooth UI:
|
|
|
1602
857
|
import { getDailyPanchang } from 'panchang-ts';
|
|
1603
858
|
import { InteractionManager } from 'react-native';
|
|
1604
859
|
|
|
1605
|
-
// Pass 1 — cheapest useful result: elements, slots, muhurtas
|
|
1606
|
-
// (~0.25 ms Node on a new date, ~0.14 ms on one already seen).
|
|
1607
|
-
// `sections` is the lever; `computeEndTimes: false` only helps once it is
|
|
1608
|
-
// narrowed, and slightly hurts on a full-section call.
|
|
860
|
+
// Pass 1 — cheapest useful result: elements, slots, muhurtas (~0.25 ms).
|
|
1609
861
|
const fast = getDailyPanchang(date, location, {
|
|
1610
862
|
timezone: 330,
|
|
1611
863
|
sections: [],
|
|
@@ -1613,7 +865,7 @@ const fast = getDailyPanchang(date, location, {
|
|
|
1613
865
|
});
|
|
1614
866
|
setState(fast);
|
|
1615
867
|
|
|
1616
|
-
// Pass 2 — background, everything (~0.41 ms
|
|
868
|
+
// Pass 2 — background, everything (~0.41 ms).
|
|
1617
869
|
InteractionManager.runAfterInteractions(() => {
|
|
1618
870
|
setState(getDailyPanchang(date, location, { timezone: 330 }));
|
|
1619
871
|
});
|
|
@@ -1623,7 +875,9 @@ InteractionManager.runAfterInteractions(() => {
|
|
|
1623
875
|
|
|
1624
876
|
## Accuracy
|
|
1625
877
|
|
|
1626
|
-
|
|
878
|
+
📖 [Full accuracy notes →](https://dharmagya.app/docs/panchang-ts/accuracy)
|
|
879
|
+
|
|
880
|
+
8,737 tests across 136 files, including fixtures cross-verified against reference
|
|
1627
881
|
panchang calculations spanning 2025–2026 across 10 Indian cities plus New York,
|
|
1628
882
|
London, Sydney, Dubai, Singapore (diaspora fixtures cover DST on
|
|
1629
883
|
`America/New_York`).
|
|
@@ -1633,213 +887,70 @@ London, Sydney, Dubai, Singapore (diaspora fixtures cover DST on
|
|
|
1633
887
|
| Sunrise / Sunset | ≤29 s observed vs reference minute-midpoint (±45 s tolerance) |
|
|
1634
888
|
| Moonrise / Moonset | Meeus apparent-upper-limb (refraction + parallax); ~3–5 min vs simpler-horizon authorities is expected |
|
|
1635
889
|
| Tithi / Nakshatra / Yoga / Karana names | Exact match vs reference |
|
|
1636
|
-
| Tithi / Nakshatra / Yoga / Karana end-times | ≤60 s vs Drik across all 20 audited comparisons
|
|
890
|
+
| Tithi / Nakshatra / Yoga / Karana end-times | ≤60 s vs Drik across all 20 audited comparisons |
|
|
1637
891
|
| Ayanamsa (Lahiri) | Reproduces DrikPanchang's published value to ~0.01″ across 1950–2050 |
|
|
1638
892
|
| Planetary positions (Sun–Saturn) | ±0.02° sidereal |
|
|
1639
|
-
| Planetary positions (Rahu/Ketu
|
|
1640
|
-
| Planetary positions (Rahu/Ketu, true node) | ≤0.6° typical (Meeus periodic correction) |
|
|
893
|
+
| Planetary positions (Rahu/Ketu) | ≤0.5° mean node, ≤0.6° true node (typical) |
|
|
1641
894
|
| Lagna sidereal longitude | Cross-checked against Jagannath Hora reference charts |
|
|
1642
895
|
| D1 / D9 house placement | Exact match vs reference for 9-graha placement |
|
|
1643
896
|
| 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) |
|
|
1644
898
|
| Sade Sati arc start/end | ±1–2 days vs authoritative ephemerides |
|
|
1645
899
|
|
|
1646
|
-
**
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
(`tests/validation/element-endtime-audit.test.ts` carries the working).
|
|
1652
|
-
|
|
1653
|
-
**Ayanamsa.** Only Lahiri is verified against an external reference — Drik
|
|
1654
|
-
publishes no value for the other four. Raman, KP, True Chitrapaksha and
|
|
1655
|
-
Thirukanitham are held at their historical offsets from Lahiri, so correcting
|
|
1656
|
-
Lahiri carried them along rather than silently changing how each relates to it.
|
|
1657
|
-
|
|
1658
|
-
**Detection notes.** **Aadal / Vidaal** follow the classical Moon-from-Sun
|
|
1659
|
-
nakshatra-distance rule (AstroShastra, HoraSarvam, Ernst Wilhelm), NOT the
|
|
1660
|
-
Tamil-Vakya weekday rule used by some online panchangs. **Varjyam** emits the
|
|
1661
|
-
sunrise-anchored nakshatra's window only. **Do Ghati Muhurta** does not rotate
|
|
1662
|
-
by weekday — the same 30-name deity-keyed sequence applies every day.
|
|
1663
|
-
|
|
1664
|
-
### Festival Detection — Documented Tradeoff
|
|
1665
|
-
|
|
1666
|
-
The library uses **tithi-at-sunrise** to resolve a festival to a calendar day.
|
|
1667
|
-
Some authorities use other classical rules for certain festivals; where those
|
|
1668
|
-
rules pick a different day, output can drift ±1 day:
|
|
1669
|
-
|
|
1670
|
-
| Alternative rule | Affects |
|
|
1671
|
-
|---|---|
|
|
1672
|
-
| Tithi-at-midnight | Krishna Janmashtami, Maha Shivaratri, Diwali / Lakshmi Puja |
|
|
1673
|
-
| Madhyahna-vyapini | Ganesh Chaturthi (edge years), Akshaya Tritiya 2026 |
|
|
1674
|
-
| Kshaya-tithi handling | Ugadi 2026-03-19 (Pratipada is Kshaya) |
|
|
900
|
+
**Festival dating** uses tithi-at-sunrise; a few festivals have authorities on
|
|
901
|
+
other rules (tithi-at-midnight for Janmashtami / Shivaratri / Diwali,
|
|
902
|
+
madhyahna-vyapini for Ganesh Chaturthi edge years) where output can drift
|
|
903
|
+
±1 day — the exact list is
|
|
904
|
+
[documented](https://dharmagya.app/docs/panchang-ts/accuracy#festival-tradeoff).
|
|
1675
905
|
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
906
|
+
**Detection conventions:** Aadal / Vidaal follow the classical Moon-from-Sun
|
|
907
|
+
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
|
|
909
|
+
tyajya spells). Do Ghati does not rotate by weekday.
|
|
1679
910
|
|
|
1680
911
|
---
|
|
1681
912
|
|
|
1682
913
|
## Performance
|
|
1683
914
|
|
|
1684
|
-
|
|
1685
|
-
processes per configuration. Treat them as relative guidance, not a spec — they
|
|
1686
|
-
move with hardware, latitude and date.
|
|
915
|
+
📖 [Full performance notes →](https://dharmagya.app/docs/panchang-ts/performance)
|
|
1687
916
|
|
|
1688
|
-
|
|
1689
|
-
calendar-scan cost
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
and benchmarked beside this one.
|
|
917
|
+
Measured at Pune, Apple M-series, Node — median of 11 processes. **Distinct
|
|
918
|
+
days** is the calendar-scan cost; **same day repeated** is what a UI
|
|
919
|
+
re-rendering one date sees. Last column is published 4.3.1, benchmarked beside
|
|
920
|
+
this release.
|
|
1693
921
|
|
|
1694
922
|
| `getDailyPanchang` call | Distinct days | Same day repeated | 4.3.1 (distinct) |
|
|
1695
923
|
|---|---|---|---|
|
|
1696
924
|
| Default (all sections + end-times) | **~0.41 ms** | **~0.17 ms** | ~6.06 ms |
|
|
1697
|
-
| `computeEndTimes: false` | ~0.39 ms | ~0.15 ms | ~5.63 ms |
|
|
1698
|
-
| Without `'festivals'` | ~0.39 ms | — | n/a |
|
|
1699
|
-
| `sections: ['festivals', 'eclipse']` | ~0.40 ms | — | n/a |
|
|
1700
|
-
| `sections: []` | ~0.27 ms | — | n/a |
|
|
1701
925
|
| `sections: []` + `computeEndTimes: false` | ~0.25 ms | ~0.14 ms | n/a |
|
|
1702
926
|
| `getInstantPanchang` | ~0.21 ms | ~0.10 ms | ~0.43 ms |
|
|
1703
927
|
|
|
1704
|
-
|
|
1705
|
-
counterpart — passing it to 4.3.1 is silently ignored and you get a full run.
|
|
1706
|
-
|
|
1707
|
-
**A default day is ~15× cheaper than in 4.3.1**, and a repeated day ~37×. Most
|
|
1708
|
-
of that is not tuning: 4.3.1 ran `astronomy-engine`'s full lunar theory inside
|
|
1709
|
-
the eclipse search on every day containing a syzygy, and had no cache that
|
|
1710
|
-
survived a call.
|
|
1711
|
-
|
|
1712
|
-
One surface moved the other way, and it is deliberate: the **raw longitude
|
|
1713
|
-
getters** (`getSiderealSunLongitude`, `getSiderealMoonLongitude`) cost about
|
|
1714
|
-
twice what they did in 4.3.1 per call — ~14 µs against ~7 µs for the Moon,
|
|
1715
|
-
~2.8 µs against ~1.4 µs for the Sun — because the own-ephemeris series keep
|
|
1716
|
-
roughly three times `astronomy-engine`'s accuracy against JPL DE441, and the
|
|
1717
|
-
evaluation is sine-bound, so more terms cost proportionally more. Every
|
|
1718
|
-
documented workflow (`getDailyPanchang`, `getInstantPanchang`, the year/range
|
|
1719
|
-
APIs, the static tables) amortizes those reads through caches and is faster
|
|
1720
|
-
than 4.3.1 by the factors above; the per-call price is only visible to code
|
|
1721
|
-
calling the raw getters in a tight loop over distinct instants. For scanning
|
|
1722
|
-
workloads, prefer the range APIs or `getDailyPanchang` — they read through
|
|
1723
|
-
interpolated longitude blocks precisely so that this cost is paid once per day
|
|
1724
|
-
rather than once per read.
|
|
1725
|
-
|
|
1726
|
-
The same trade surfaces once more in the birth-chart *primitives*:
|
|
1727
|
-
`computeRashiChart` and `computeNavamsa` measure ~0.31 ms against ~0.09 ms on
|
|
1728
|
-
published 4.3.1 — a chart is fifteen full-accuracy planet evaluations (three
|
|
1729
|
-
per planet, for the retrograde probes) and nothing amortizes them. The deeper
|
|
1730
|
-
chart stack inverts it again: `computeShadbala` and `computeBhavaBala` come
|
|
1731
|
-
out ~2× *faster* than 4.3.1, because v5 computes the positions once and reuses
|
|
1732
|
-
them. At a third of a millisecond per chart this is irrelevant interactively;
|
|
1733
|
-
it is visible only to code building thousands of charts in a batch.
|
|
1734
|
-
|
|
1735
|
-
Cost is dominated by ephemeris evaluations, so the lever that matters is the one
|
|
1736
|
-
that avoids them:
|
|
1737
|
-
|
|
1738
|
-
- **`sections`** — skip the optional ephemeris-backed blocks you don't need.
|
|
1739
|
-
Dropping `'festivals'` takes a default call from ~0.41 ms to ~0.39 ms cold,
|
|
1740
|
-
and dropping everything takes it to ~0.27 ms. See
|
|
1741
|
-
[Narrowing the work](#narrowing-the-work).
|
|
1742
|
-
- **`computeEndTimes: false`** — a small win, never a large one. It skips the
|
|
1743
|
-
transition searches, but those read through the same interpolated longitude
|
|
1744
|
-
blocks the rest of the call has already built, so what it saves is arithmetic
|
|
1745
|
-
rather than ephemeris: ~5% cold, ~10% warm. Use it to drop `endTime` fields
|
|
1746
|
-
you don't want, not to go faster.
|
|
1747
|
-
|
|
1748
|
-
*Changed in v5.* Both entry points now always interpolate, so output depends
|
|
1749
|
-
on neither `sections` nor `computeEndTimes` — see `INTERPOLATE_ALWAYS` in
|
|
1750
|
-
`src/core/panchang.ts`. Earlier development builds chose the longitude cache's
|
|
1751
|
-
mode from `computeEndTimes`, which made asking for *less* output cost *more*
|
|
1752
|
-
on a full call; that is gone.
|
|
1753
|
-
|
|
1754
|
-
Repeated calls for the same location-day are cheaper because solar rise/set
|
|
1755
|
-
events are cached process-wide, keyed on `(direction, lat, lon, elevation, UTC
|
|
1756
|
-
day)` and bounded at 20,000 entries. The cache makes sunrise single-valued as
|
|
1757
|
-
well as fast — see [Upgrading from 4.x](#sunrise-is-single-valued-per-location-day).
|
|
1758
|
-
It does not make a *single* cold rise/set call cheaper — against 4.3.1
|
|
1759
|
-
`getSunrise` is +1.8%, `getSunset` +8.8%, `getMoonrise` +5.5% and `getMoonset`
|
|
1760
|
-
+6.6%, i.e. unchanged to slightly worse — what it removes is the second and
|
|
1761
|
-
every later call for the same day.
|
|
1762
|
-
|
|
1763
|
-
Range helpers apply the same narrowing internally:
|
|
1764
|
-
`computeEkadashiDatesForYear` reads only the tithi at sunrise and so runs with
|
|
1765
|
-
every optional section off (**~18 ms** for a full year, against ~2,360 ms in
|
|
1766
|
-
4.3.1); `computeFestivalsInRange` keeps only `'festivals'` and `'eclipse'`
|
|
1767
|
-
(**~131 ms/year**, against ~2,180); `computeSankrantisForYear` needs only the
|
|
1768
|
-
Sun, so it scans one solar longitude per day and bisects the 12 transits rather
|
|
1769
|
-
than building a panchang each day (**~3.3 ms/year**, against ~154).
|
|
1770
|
-
|
|
1771
|
-
Birth-chart helpers are independent — calling them does not add work to
|
|
1772
|
-
`getDailyPanchang`. Within them, `computeShadbala` and `computeBhavaBala` build
|
|
1773
|
-
the natal positions once and derive all seven charts from them (~0.33 ms each,
|
|
1774
|
-
against ~0.72 in 4.3.1).
|
|
1775
|
-
|
|
1776
|
-
**Charts are the one place v5 is slower.** `computeRashiChart` and
|
|
1777
|
-
`computeNavamsa` cost **~0.32 ms** against ~0.10 in 4.3.1 — 3.3×, entirely the
|
|
1778
|
-
planetary ephemeris, and the deliberate price of an order-of-magnitude accuracy
|
|
1779
|
-
gain against JPL DE441 (Mercury 6.50″ → 0.30″, Venus 19.59″ → 0.86″). If you
|
|
1780
|
-
build many charts and do not need that precision, 4.x was cheaper; nothing else
|
|
1781
|
-
in the library regressed.
|
|
1782
|
-
|
|
1783
|
-
### Narrowing the work
|
|
1784
|
-
|
|
1785
|
-
`PanchangSection` lists the four optional blocks. Everything else a daily
|
|
1786
|
-
panchang returns — the five elements, slot systems, muhurtas, inauspicious
|
|
1787
|
-
periods, masa / samvat / rashi — is arithmetic over the sunrise / sunset /
|
|
1788
|
-
next-sunrise triplet and is always computed, because skipping it would save
|
|
1789
|
-
nothing.
|
|
1790
|
-
|
|
1791
|
-
| Section | Covers | Fields when omitted |
|
|
1792
|
-
|---|---|---|
|
|
1793
|
-
| `'festivals'` | Festival detection — needs the prior day's sunrise/sunset, the next day's transit, per-kala tithi anchors, and the prior day's Chandra Masa | `festivals: []` — but an eclipse entry is still prepended when `'eclipse'` is on |
|
|
1794
|
-
| `'eclipse'` | Eclipse overlapping the Hindu day | `eclipse: null` |
|
|
1795
|
-
| `'moonTimes'` | `moon.rise` / `moon.set` | `null` |
|
|
1796
|
-
| `'lunarWindows'` | Bhadra, Varjyam, Panchaka-Rahita — each binary-searches lunar longitude across the day | `null` / `[]` |
|
|
1797
|
-
|
|
1798
|
-
```typescript
|
|
1799
|
-
// Everything (default).
|
|
1800
|
-
getDailyPanchang(date, loc, { timezone: 330 });
|
|
1801
|
-
|
|
1802
|
-
// Festivals only — no moon times, no Bhadra/Varjyam windows.
|
|
1803
|
-
getDailyPanchang(date, loc, {
|
|
1804
|
-
timezone: 330,
|
|
1805
|
-
sections: ['festivals', 'eclipse'],
|
|
1806
|
-
});
|
|
1807
|
-
|
|
1808
|
-
// Cheapest useful call: elements, slots, muhurtas and inauspicious periods
|
|
1809
|
-
// only. Those are arithmetic on the sunrise triplet and are always computed.
|
|
1810
|
-
getDailyPanchang(date, loc, {
|
|
1811
|
-
timezone: 330,
|
|
1812
|
-
sections: [],
|
|
1813
|
-
computeEndTimes: false,
|
|
1814
|
-
});
|
|
1815
|
-
```
|
|
928
|
+
**A default day is ~15× cheaper than 4.3.1**, a repeated day ~37×. The levers:
|
|
1816
929
|
|
|
1817
|
-
|
|
1818
|
-
|
|
930
|
+
- **`sections`** — skip the optional ephemeris-backed blocks (`'festivals'`,
|
|
931
|
+
`'eclipse'`, `'moonTimes'`, `'lunarWindows'`). Narrowing is exactly
|
|
932
|
+
output-neutral: every field a narrowed call computes is identical to the full
|
|
933
|
+
call's; omitted sections sit at their documented `null` / `[]`.
|
|
934
|
+
- **`computeEndTimes: false`** — drops the `endTime` transition searches;
|
|
935
|
+
~5–10% — use it to drop fields you don't want, not to go faster.
|
|
1819
936
|
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
narrowing could shift transition times by up to 63 s.)
|
|
937
|
+
Range helpers narrow internally: `computeEkadashiDatesForYear` **~18 ms/year**
|
|
938
|
+
(4.3.1: ~2,360), `computeFestivalsInRange` **~131 ms/year** (~2,180),
|
|
939
|
+
`computeSankrantisForYear` **~3.3 ms/year** (~154). Solar rise/set events are
|
|
940
|
+
cached process-wide (bounded, 20k entries), which also makes them
|
|
941
|
+
single-valued.
|
|
1826
942
|
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
`hermes-parser` over the built bundle to prove the syntax is Hermes-compatible,
|
|
1832
|
-
which is a *parse* check and does not execute anything. Hermes numbers will be
|
|
1833
|
-
published here once they are actually measured on device.
|
|
1834
|
-
|
|
1835
|
-
What does carry over is the shape of the cost: it is dominated by ephemeris
|
|
1836
|
-
math, so the `sections` and `computeEndTimes` levers above have the same
|
|
1837
|
-
proportional effect on any runtime.
|
|
943
|
+
One deliberate regression: raw chart primitives (`computeRashiChart`,
|
|
944
|
+
`computeNavamsa`) cost ~0.32 ms vs ~0.10 in 4.3.1 — the price of an
|
|
945
|
+
order-of-magnitude accuracy gain against JPL DE441. `computeShadbala` /
|
|
946
|
+
`computeBhavaBala` went the other way, ~2× faster.
|
|
1838
947
|
|
|
1839
948
|
---
|
|
1840
949
|
|
|
1841
950
|
## Error Handling
|
|
1842
951
|
|
|
952
|
+
📖 [Errors & Compatibility →](https://dharmagya.app/docs/panchang-ts/errors)
|
|
953
|
+
|
|
1843
954
|
```typescript
|
|
1844
955
|
import { PanchangError } from 'panchang-ts';
|
|
1845
956
|
|
|
@@ -1854,7 +965,7 @@ try {
|
|
|
1854
965
|
```
|
|
1855
966
|
|
|
1856
967
|
Error codes: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
|
|
1857
|
-
`INVALID_ELEVATION`, `INVALID_TIMEZONE`, `INVALID_AYANAMSA`,
|
|
968
|
+
`INVALID_ELEVATION`, `INVALID_TIMEZONE`, `INVALID_AYANAMSA`, `INVALID_INPUT`,
|
|
1858
969
|
`TIMEZONE_RESOLUTION_FAILED`, `NO_SUNRISE`, `NO_SUNSET`, `SEARCH_DIVERGED`,
|
|
1859
970
|
`CIRCUMPOLAR` (Placidus-KP houses above ±66.5°).
|
|
1860
971
|
|
|
@@ -1871,18 +982,25 @@ for the Moon).
|
|
|
1871
982
|
|
|
1872
983
|
| Environment | Support |
|
|
1873
984
|
|---|---|
|
|
1874
|
-
| Node.js
|
|
985
|
+
| Node.js ≥ 22 (per the package `engines` field) | Supported |
|
|
1875
986
|
| React Native (Hermes) | Supported (pass `timezone` as number) |
|
|
1876
987
|
| Expo (managed + bare) | Supported |
|
|
1877
988
|
| Browser (modern, ESM) | Supported |
|
|
1878
989
|
| Browser (legacy / IE) | Not supported |
|
|
1879
990
|
|
|
991
|
+
The build targets ES2020 (ESM + CJS, full `.d.ts`), has **zero runtime
|
|
992
|
+
dependencies**, and is `sideEffects: false`.
|
|
993
|
+
|
|
1880
994
|
---
|
|
1881
995
|
|
|
1882
996
|
## Acknowledgements
|
|
1883
997
|
|
|
1884
|
-
|
|
1885
|
-
|
|
998
|
+
As of v5 the package has **no runtime dependencies** — the ephemeris, ΔT model
|
|
999
|
+
and event searches are the library's own. Two projects still deserve credit:
|
|
1000
|
+
[astronomy-engine](https://github.com/cosinekitty/astronomy) by Don Cross (MIT),
|
|
1001
|
+
the runtime engine through 4.x and now the dev-time baseline the own ephemeris
|
|
1002
|
+
is tested against, and the algorithms of Jean Meeus's *Astronomical Algorithms*,
|
|
1003
|
+
which underpin the rise/set and node models.
|
|
1886
1004
|
|
|
1887
1005
|
## License
|
|
1888
1006
|
|