panchang-ts 4.3.1 → 5.0.1
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 +566 -927
- package/dist/calendar/eclipsesTable.cjs +13 -358
- package/dist/calendar/eclipsesTable.d.cts +33 -23
- package/dist/calendar/eclipsesTable.d.ts +33 -23
- package/dist/calendar/eclipsesTable.js +10 -357
- package/dist/calendar/festivalsTable.cjs +48 -25335
- package/dist/calendar/festivalsTable.d.cts +33 -23
- package/dist/calendar/festivalsTable.d.ts +33 -23
- package/dist/calendar/festivalsTable.js +45 -25334
- package/dist/calendar/moonPhasesTable.cjs +55 -6791
- package/dist/calendar/moonPhasesTable.d.cts +33 -21
- package/dist/calendar/moonPhasesTable.d.ts +33 -21
- package/dist/calendar/moonPhasesTable.js +52 -6790
- package/dist/{eclipsesTableTypes-D-tSQMQd.d.cts → eclipsesTableTypes-BFYWRA44.d.cts} +11 -1
- package/dist/{eclipsesTableTypes-D-tSQMQd.d.ts → eclipsesTableTypes-BFYWRA44.d.ts} +11 -1
- package/dist/festivalsTableTypes-CzDgY4zS.d.cts +100 -0
- package/dist/festivalsTableTypes-CzDgY4zS.d.ts +100 -0
- package/dist/index.cjs +29098 -5938
- package/dist/index.d.cts +1102 -255
- package/dist/index.d.ts +1102 -255
- package/dist/index.js +29081 -5939
- package/dist/moonPhasesTableTypes-9XLibNCZ.d.cts +103 -0
- package/dist/moonPhasesTableTypes-9XLibNCZ.d.ts +103 -0
- package/dist/muhurta/muhurtaTable.cjs +53 -0
- package/dist/muhurta/muhurtaTable.d.cts +47 -0
- package/dist/muhurta/muhurtaTable.d.ts +47 -0
- package/dist/muhurta/muhurtaTable.js +47 -0
- package/dist/muhurtaTableTypes-CgBom297.d.cts +81 -0
- package/dist/muhurtaTableTypes-CgBom297.d.ts +81 -0
- package/package.json +23 -6
- package/dist/festivalsTableTypes-GePaspwd.d.cts +0 -52
- package/dist/festivalsTableTypes-GePaspwd.d.ts +0 -52
- package/dist/moonPhasesTableTypes-TH0T5rVD.d.cts +0 -56
- package/dist/moonPhasesTableTypes-TH0T5rVD.d.ts +0 -56
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.
|
|
9
|
+
**Fast** (~0.25 ms trimmed, ~0.41 ms full) · **Typed** (full TypeScript) · **Offline** (pure JS math) · **8,368 tests across 121 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 4.x → 5 migration in full, plus a per-feature
|
|
13
|
+
> quick reference. The complete reference — every option, result field, table format, accuracy
|
|
14
|
+
> bound and performance note — lives on the docs site.
|
|
9
15
|
|
|
10
16
|
---
|
|
11
17
|
|
|
@@ -31,26 +37,84 @@ const result = getDailyPanchang(
|
|
|
31
37
|
// → DailyPanchangResult | null. Null only at polar latitudes where
|
|
32
38
|
// sunrise can't be computed. Anywhere else, narrow with `if (!result) return;`.
|
|
33
39
|
|
|
34
|
-
console.log(result!.tithis[0].name); // "Krishna Chaturdashi"
|
|
35
|
-
console.log(result!.nakshatras[0].name); // "Mrigashira"
|
|
36
|
-
console.log(result!.vara.name); // "Mangalawara"
|
|
37
|
-
console.log(result!.chandramasa.name); // "Magha"
|
|
38
|
-
console.log(result!.samvat.vikramSamvat); // 2081
|
|
40
|
+
console.log(result!.angas.tithis[0].name); // "Krishna Chaturdashi"
|
|
41
|
+
console.log(result!.angas.nakshatras[0].name); // "Mrigashira"
|
|
42
|
+
console.log(result!.angas.vara.name); // "Mangalawara"
|
|
43
|
+
console.log(result!.calendar.chandramasa.name); // "Magha"
|
|
44
|
+
console.log(result!.calendar.samvat.vikramSamvat); // 2081
|
|
39
45
|
```
|
|
40
46
|
|
|
41
47
|
### Reading Output Times
|
|
42
48
|
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
Every `Date` in a result is a **real instant** — `.getTime()` is the correct
|
|
50
|
+
epoch millisecond. Every instant has a `*Local` companion: an offset-carrying
|
|
51
|
+
ISO 8601 string, which is what you want for display.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
result!.sun.rise; // Date — 2025-01-14T01:39:44.172Z (the actual moment)
|
|
55
|
+
result!.sun.riseLocal; // "2025-01-14T07:09:44.172+05:30"
|
|
56
|
+
result!.inauspicious.rahuKalam.start; // Date
|
|
57
|
+
result!.inauspicious.rahuKalam.startLocal;
|
|
58
|
+
|
|
59
|
+
// Just the wall clock:
|
|
60
|
+
result!.sun.riseLocal.slice(11, 16); // "07:09"
|
|
61
|
+
|
|
62
|
+
// Anything else works too, because the Date is genuinely correct:
|
|
63
|
+
new Intl.DateTimeFormat('en-IN', { timeZone: 'Asia/Kolkata', timeStyle: 'short' })
|
|
64
|
+
.format(result!.sun.rise); // "7:09 am"
|
|
65
|
+
Temporal.Instant.from(result!.sun.riseLocal);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
For an instant you derive yourself, `formatInZone` renders it the same way:
|
|
45
69
|
|
|
46
70
|
```typescript
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
71
|
+
import { formatInZone } from 'panchang-ts';
|
|
72
|
+
const noon = new Date((result!.sun.rise.getTime() + result!.sun.set.getTime()) / 2);
|
|
73
|
+
formatInZone(noon, result!.timezone.offsetMinutes); // "2025-01-14T12:27:31.086+05:30"
|
|
50
74
|
```
|
|
51
75
|
|
|
52
|
-
|
|
53
|
-
|
|
76
|
+
> **Changed in v5 — this is the breaking change most likely to affect you.**
|
|
77
|
+
> Through 4.x every published `Date` was the true instant *shifted* by the UTC
|
|
78
|
+
> offset, and the README told you to read it back with `getUTC*`. That worked
|
|
79
|
+
> only as long as you did nothing else with the value: `JSON.stringify` emitted
|
|
80
|
+
> a wrong instant labelled `Z`, `Intl` with a `timeZone` rendered 12:39 pm for
|
|
81
|
+
> an 07:09 am sunrise, and any comparison, diff, database write, date-fns or
|
|
82
|
+
> Temporal call was off by the offset.
|
|
83
|
+
>
|
|
84
|
+
> Migration is mechanical: `x.getUTCHours()` → read `xLocal`, or format the
|
|
85
|
+
> instant. See [Upgrading from 4.x](#upgrading-from-4x).
|
|
86
|
+
|
|
87
|
+
`moon.rise` / `moon.set` can be `null` — the Moon occasionally does not rise or
|
|
88
|
+
set on a given calendar day, which is normal. `moon.riseLocal` / `moon.setLocal`
|
|
89
|
+
are `null` exactly when they are.
|
|
90
|
+
|
|
91
|
+
### The result is grouped
|
|
92
|
+
|
|
93
|
+
`DailyPanchangResult` has seven groups plus a handful of top-level fields.
|
|
94
|
+
Through 4.x it was ~50 flat fields; the groups are what tell you where to look.
|
|
95
|
+
|
|
96
|
+
| Group | Holds |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `sun` | `rise` / `set` / `nextRise` (+ `*Local`), day and night lengths, the Sun's `siderealLongitude` and `nakshatra` |
|
|
99
|
+
| `moon` | `rise` / `set` (+ `*Local`), the Moon's `siderealLongitude` and `rashi` |
|
|
100
|
+
| `angas` | the five limbs — `tithis`, `nakshatras`, `yogas`, `karanas`, `vara` |
|
|
101
|
+
| `calendar` | `masa` (solar), `chandramasa` (lunar), `samvat` |
|
|
102
|
+
| `muhurtas` | `abhijit`, `brahma`, `vijaya`, `godhuli`, `nishita`, `amritKala`, `madhyahna`, `pratahSandhya`, `sayahnaSandhya`, `doGhati` |
|
|
103
|
+
| `inauspicious` | `rahuKalam`, `gulikaKalam`, `yamaganda`, `durMuhurta`, `varjyam`, `bhadra`, `gandaMula`, `panchaka`, `panchakaInfo`, `panchakaRahita` |
|
|
104
|
+
| `periods` | `choghadiya`, `hora`, `gowri` |
|
|
105
|
+
|
|
106
|
+
Top level: `date`, `location`, `timezone`, `ayanamsa`, `specialYogas`,
|
|
107
|
+
`anandadiYoga`, `festivals`, `eclipse`, `chandraBalam`, `tarabala`.
|
|
108
|
+
|
|
109
|
+
**Nothing is optional.** Every field is always present. A value that does not
|
|
110
|
+
apply is `null`; a collection that does not apply is `[]`. That holds whether
|
|
111
|
+
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
|
|
113
|
+
depends on what you passed.
|
|
114
|
+
|
|
115
|
+
`getInstantPanchang` uses the same group names for the subset an instant can
|
|
116
|
+
answer: `sun`, `moon`, `angas`, `calendar`, `inauspicious`. There is no
|
|
117
|
+
`muhurtas` or `periods`, because those are properties of a Hindu *day*.
|
|
54
118
|
|
|
55
119
|
### `getDailyPanchang` vs `getInstantPanchang`
|
|
56
120
|
|
|
@@ -66,6 +130,327 @@ Ekadashi split. For reliable festival dating, use `getDailyPanchang`.
|
|
|
66
130
|
|
|
67
131
|
---
|
|
68
132
|
|
|
133
|
+
## Upgrading from 4.x
|
|
134
|
+
|
|
135
|
+
Three changes move numbers that 4.x produced, and one option is gone.
|
|
136
|
+
|
|
137
|
+
### Lahiri ayanamsa corrected by +38″
|
|
138
|
+
|
|
139
|
+
The library's Lahiri constant sat 38 arcseconds behind DrikPanchang's — it used
|
|
140
|
+
`23.853211°` at J2000 (the widely-repeated 23° 51′ 11.6″ figure) where Drik
|
|
141
|
+
computes `23.863801°`. The replacement was solved from Drik's own published
|
|
142
|
+
values across 1950–2050, which agree on it to within 0.01″ — a century-wide
|
|
143
|
+
baseline, so the precession polynomial is pinned too, not just the epoch
|
|
144
|
+
constant. Every sidereal output moves with it:
|
|
145
|
+
|
|
146
|
+
| Output | Effect |
|
|
147
|
+
|---|---|
|
|
148
|
+
| Nakshatra end-times | ~69 s later than 4.x (carries the ayanamsa once) |
|
|
149
|
+
| Yoga end-times | ~129 s later than 4.x (carries it twice) |
|
|
150
|
+
| Planetary longitudes, rashi, pada, lagna, divisionals, dashas | shifted +0.0106° |
|
|
151
|
+
| Tithi / karana end-times | unchanged — Moon − Sun cancels the ayanamsa |
|
|
152
|
+
| Raman / KP / True Chitra / Thirukanitham | moved by the same +38″; their offsets from Lahiri are preserved |
|
|
153
|
+
|
|
154
|
+
Worst-case end-time drift vs Drik dropped from 131 s to 60 s, and the sign split
|
|
155
|
+
by ayanamsa exposure — nakshatra and yoga early, tithi and karana late — is gone.
|
|
156
|
+
If you have snapshot tests or cached charts from 4.x, expect them to need
|
|
157
|
+
re-pinning.
|
|
158
|
+
|
|
159
|
+
### ΔT now uses measurement, so every published time moves ~6 s
|
|
160
|
+
|
|
161
|
+
4.x took ΔT (TT − UT) entirely from Espenak–Meeus. Its post-2005 branches are an
|
|
162
|
+
extrapolation published in 2006, and Earth's rotation did not follow it — by
|
|
163
|
+
2026 the model reads about **5.9 s high**, drifting a further ~0.6 s each year.
|
|
164
|
+
Because this library reports *times*, that lands directly on published values.
|
|
165
|
+
|
|
166
|
+
v5 takes ΔT from the leap-second chain (`32.184 + (TAI − UTC)`, exact, and
|
|
167
|
+
within the 0.9 s band leap seconds maintain) wherever ΔT has actually been
|
|
168
|
+
measured, and resumes Espenak–Meeus beyond it carrying the offset it had
|
|
169
|
+
accrued. Against JPL Horizons the measured era now agrees to **0.005 s** at
|
|
170
|
+
every decade from 1980, where 4.x was seconds out.
|
|
171
|
+
|
|
172
|
+
| Output | Effect |
|
|
173
|
+
|---|---|
|
|
174
|
+
| Tithi / nakshatra / yoga / karana end-times | ~5.7 s later for 2025 dates, growing with the model's drift |
|
|
175
|
+
| Sankranti and other transit instants | same shift — it is one uniform correction, not per-element |
|
|
176
|
+
| Sunrise / sunset / moonrise / moonset | barely moved — the error scales against the 15°/hr sky rotation |
|
|
177
|
+
| Dates a panchang element is *filed under* | unchanged except where a transit sits within seconds of sunrise |
|
|
178
|
+
|
|
179
|
+
That last row is the one to know about. `computeSankrantisForYear` publishes a
|
|
180
|
+
date, and the date is decided by whether the transit precedes sunrise. The 2025
|
|
181
|
+
Tula Sankranti at Reykjavik is such a case: it still falls on Oct 16, but its
|
|
182
|
+
margin narrowed from 7.1 s to 1.4 s. Locations at high latitude with a transit
|
|
183
|
+
near sunrise are where a day could flip.
|
|
184
|
+
|
|
185
|
+
### Instant-mode vara was wrong after ~19:00
|
|
186
|
+
|
|
187
|
+
`getInstantPanchang` located sunrise by searching forward from `date − 12 h`.
|
|
188
|
+
For an evening instant that start point is already past the morning's sunrise,
|
|
189
|
+
so it found *tomorrow's* and rolled the weekday back a day. Any query after
|
|
190
|
+
roughly 7 pm returned the previous vara — and with it the wrong Rahu Kalam,
|
|
191
|
+
Gulika Kalam, Yamaganda, Choghadiya, Hora, Anandadi yoga and special yogas.
|
|
192
|
+
`getDailyPanchang` was never affected. If you cached instant-mode results from
|
|
193
|
+
4.x for evening timestamps, discard them.
|
|
194
|
+
|
|
195
|
+
### `precision` removed
|
|
196
|
+
|
|
197
|
+
`precision: 'standard' | 'high'` and the `Precision` type no longer exist.
|
|
198
|
+
Element transitions are now solved by secant iteration, which converges to the
|
|
199
|
+
root rather than stopping at a fixed tolerance, so there is nothing left for the
|
|
200
|
+
option to select — and the tighter setting no longer buys anything. Removing it
|
|
201
|
+
from your options object is the whole migration; leaving it in is a type error,
|
|
202
|
+
not a silent no-op.
|
|
203
|
+
|
|
204
|
+
### Sunrise is single-valued per location-day
|
|
205
|
+
|
|
206
|
+
Solar rise/set is computed from a canonical anchor and cached per location-day,
|
|
207
|
+
so it no longer depends on which instant the caller happened to start searching
|
|
208
|
+
from. Values shift by ≤108 ms vs 4.x, and two calls for the same day now agree
|
|
209
|
+
exactly instead of differing by up to 109 ms. Windows derived proportionally
|
|
210
|
+
from the day length — Varjyam, Bhadra, the slot systems — move by a little more
|
|
211
|
+
than that. This removes an inconsistency rather than introducing an
|
|
212
|
+
approximation: 4.x returned a different sunrise depending on which caller asked.
|
|
213
|
+
|
|
214
|
+
### Moonrise and moonset are single-valued per location-day
|
|
215
|
+
|
|
216
|
+
The same treatment sunrise received, now applied to the Moon. `getMoonrise` /
|
|
217
|
+
`getMoonset` resolve through a canonical per-UTC-day cache, so an event has one
|
|
218
|
+
timestamp no matter which caller asks or from which instant they searched.
|
|
219
|
+
Published `moon.rise` / `moon.set` shift by **≤182 ms** vs 4.x. Nothing else in the
|
|
220
|
+
result moves — verified over 11,520 daily results across six locations and three
|
|
221
|
+
centuries: zero changes to any index, name, boolean, festival date or other
|
|
222
|
+
timestamp.
|
|
223
|
+
|
|
224
|
+
### `read*` for tables, `compute*` for the engine
|
|
225
|
+
|
|
226
|
+
`getFestivalsForYear` read a pre-built table; `getFestivalsInRange` ran the
|
|
227
|
+
engine. Two near-identical names, completely different inputs and semantics. v5
|
|
228
|
+
settles one convention across all four families — festivals, eclipses, moon
|
|
229
|
+
phases and muhurta:
|
|
230
|
+
|
|
231
|
+
| 4.x | v5 | what it does |
|
|
232
|
+
|---|---|---|
|
|
233
|
+
| `getFestivalsForYear` | `readFestivalsForYear` | reads a table |
|
|
234
|
+
| `getFestivalsForDate` | `readFestivalsForDate` | reads a table |
|
|
235
|
+
| `getFestivalsYearRange` | `readFestivalsYearRange` | reads a table |
|
|
236
|
+
| `getEclipsesForYear` / `ForDate` / `YearRange` | `readEclipsesForYear` / … | reads a table |
|
|
237
|
+
| `getMoonPhasesForYear` / `ForDate` / `YearRange` | `readMoonPhasesForYear` / … | reads a table |
|
|
238
|
+
| — | `readMuhurtaForYear` / `ForDate` / `YearRange` | **new** — reads a table |
|
|
239
|
+
| — | `readBestMuhurtaDays` | **new** — top-scoring days |
|
|
240
|
+
| `getFestivalsInRange` | `computeFestivalsInRange` | runs the engine |
|
|
241
|
+
| `getEclipsesInRange` | `computeEclipsesInRange` | runs the engine |
|
|
242
|
+
| `getMoonPhasesInRange` | `computeMoonPhasesInRange` | runs the engine |
|
|
243
|
+
| `findAuspiciousDates` | `computeAuspiciousDatesInRange` | runs the engine |
|
|
244
|
+
| `getEkadashiDatesForYear` | `computeEkadashiDatesForYear` | runs the engine |
|
|
245
|
+
| `getSankrantisForYear` | `computeSankrantisForYear` | runs the engine |
|
|
246
|
+
|
|
247
|
+
**Every 4.x name still works** — they are deprecated aliases pointing at the same
|
|
248
|
+
functions, kept through v5. Nothing breaks today; the old names will go in v6.
|
|
249
|
+
|
|
250
|
+
New single-year entry points, the shape most callers reach for first:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
computeFestivalsForYear(2027, location, { timezone: 330 });
|
|
254
|
+
computeEclipsesForYear(2027, location, { timezone: 330 });
|
|
255
|
+
computeMoonPhasesForYear(2027, { timezone: 330 });
|
|
256
|
+
computeAuspiciousDatesForYear(2027, vivahRule, location, { timezone: 330 });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### `buildMuhurtaTable` + the `panchang-ts/muhurta` subpath
|
|
260
|
+
|
|
261
|
+
Festivals, eclipses and moon phases each had a builder and an engine-free
|
|
262
|
+
reader; muhurta had neither, so finding auspicious dates meant running the full
|
|
263
|
+
engine on device for every query. v5 completes the family:
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
// build once (build time, or first launch), then persist the JSON
|
|
267
|
+
import { buildMuhurtaTable, vivahRule } from 'panchang-ts';
|
|
268
|
+
const table = buildMuhurtaTable({
|
|
269
|
+
rule: vivahRule, location: DELHI, timezoneOffsetMinutes: 330,
|
|
270
|
+
startYear: 2026, endYear: 2031,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// read it back with no astronomy code in the bundle (~1.7 KB)
|
|
274
|
+
import { readBestMuhurtaDays } from 'panchang-ts/muhurta';
|
|
275
|
+
readBestMuhurtaDays(table, 5); // top 5 days, highest score first
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Only days that pass the rule are stored unless you pass `includeFailures: true`.
|
|
279
|
+
|
|
280
|
+
### Built tables are dictionary-encoded and carry `key`
|
|
281
|
+
|
|
282
|
+
`build*Table` now emits a `_dict` of unique entries with each day holding
|
|
283
|
+
indices, rather than repeating every localized string at every occurrence. A
|
|
284
|
+
10-year festival table goes from 315 KB to **89.9 KB (28.5%)**; a 10-year
|
|
285
|
+
moon-phase table from 106 KB to **29.7 KB (28.0%)**. Resolved output is
|
|
286
|
+
identical — verified across every year and both locales.
|
|
287
|
+
|
|
288
|
+
Gzip already hid most of this on the wire, so the win is **parse time and
|
|
289
|
+
resident memory**, which is the constraint that actually bites on Hermes.
|
|
290
|
+
|
|
291
|
+
Festival table entries also gained the stable `key` the engine has been
|
|
292
|
+
returning since 4.x, so a table is no longer both larger *and* less useful than
|
|
293
|
+
engine output.
|
|
294
|
+
|
|
295
|
+
**Tables you already cached still read.** The reader detects the format, so a v1
|
|
296
|
+
table built with 4.x keeps working; only `key` is unavailable from it, and comes
|
|
297
|
+
back as `''`.
|
|
298
|
+
|
|
299
|
+
### Published `Date`s are real instants — the flagship change
|
|
300
|
+
|
|
301
|
+
```diff
|
|
302
|
+
- result.sunrise.getTime() // NOT when sunrise happened (off by the UTC offset)
|
|
303
|
+
- result.sunrise.getUTCHours() // the documented 4.x idiom
|
|
304
|
+
+ result.sun.rise.getTime() // correct epoch ms
|
|
305
|
+
+ result.sun.riseLocal // "2025-01-14T07:09:44.172+05:30"
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Applies to **every** `Date` in `DailyPanchangResult` and to every `TimePeriod` —
|
|
309
|
+
`sun.rise`, `sun.set`, `sun.nextRise`, `moon.rise`, `moon.set`, all the muhurtas
|
|
310
|
+
and inauspicious periods, all four slot systems, the element
|
|
311
|
+
`startTime`/`endTime` arrays, the eclipse contacts and sutak window. Each gains
|
|
312
|
+
a `*Local` companion: `sun.riseLocal`, `inauspicious.rahuKalam.startLocal`,
|
|
313
|
+
`angas.tithis[0].endTimeLocal`, and so on.
|
|
314
|
+
|
|
315
|
+
| you had | you now write |
|
|
316
|
+
|---|---|
|
|
317
|
+
| `r.sun.rise.getUTCHours()` | `r.sun.riseLocal.slice(11, 13)` |
|
|
318
|
+
| `` `${h}:${m}` `` from `getUTC*` | `r.sun.riseLocal.slice(11, 16)` |
|
|
319
|
+
| `r.inauspicious.rahuKalam.start.getUTCHours()` | `r.inauspicious.rahuKalam.startLocal.slice(11, 13)` |
|
|
320
|
+
| `r.angas.tithis[0].endTime` for display | `r.angas.tithis[0].endTimeLocal` |
|
|
321
|
+
| a `Date` you derived yourself | `formatInZone(d, r.timezone.offsetMinutes)` |
|
|
322
|
+
|
|
323
|
+
`getInstantPanchang` results carry **no** `*Local` fields: that call takes no
|
|
324
|
+
timezone, so there is no zone to render a wall clock in.
|
|
325
|
+
|
|
326
|
+
**Cost:** rendering the strings adds ~0.04 ms per daily panchang — invisible on
|
|
327
|
+
a cold call and ~20% of a fully cached warm one. The release warms to
|
|
328
|
+
**0.17 ms**, against published 4.3.1's **6.20 ms**.
|
|
329
|
+
|
|
330
|
+
### `result.timezone` is now an object
|
|
331
|
+
|
|
332
|
+
```diff
|
|
333
|
+
- result.timezone // 330
|
|
334
|
+
+ result.timezone // { offsetMinutes: 330, zone: 'Asia/Kolkata' }
|
|
335
|
+
+ result.timezone.offsetMinutes
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
`options.timezone` has always accepted `number | string`, but the result carried
|
|
339
|
+
only a number — so passing `'America/New_York'` produced a result that could not
|
|
340
|
+
say which zone produced it. `zone` is present only when you passed a zone name.
|
|
341
|
+
|
|
342
|
+
**The DST limit, now stated explicitly.** The offset is resolved **once per
|
|
343
|
+
call** from a reference date, so a Hindu day containing a DST transition is
|
|
344
|
+
computed at a single offset throughout. Correct for almost every day; on the one
|
|
345
|
+
or two transition days a year, times after the jump are shifted by its size.
|
|
346
|
+
4.x documented this as a blanket "DST resolves automatically", which was not the
|
|
347
|
+
whole truth.
|
|
348
|
+
|
|
349
|
+
### The result object is grouped
|
|
350
|
+
|
|
351
|
+
`DailyPanchangResult` had ~50 flat top-level fields mixing five categories.
|
|
352
|
+
v5 sorts them into seven groups — see [The result is grouped](#the-result-is-grouped)
|
|
353
|
+
for the full table. This is a large break, and it lands in the same release as
|
|
354
|
+
the `Date` change on purpose: migrating both at once is one pass over your read
|
|
355
|
+
sites, not two.
|
|
356
|
+
|
|
357
|
+
Every rename, in full:
|
|
358
|
+
|
|
359
|
+
| 4.x | v5 |
|
|
360
|
+
|---|---|
|
|
361
|
+
| `sunrise` / `sunset` / `nextSunrise` | `sun.rise` / `sun.set` / `sun.nextRise` |
|
|
362
|
+
| `sunriseLocal` / `sunsetLocal` / `nextSunriseLocal` | `sun.riseLocal` / `sun.setLocal` / `sun.nextRiseLocal` |
|
|
363
|
+
| `dayDurationMinutes` / `nightDurationMinutes` | `sun.dayDurationMinutes` / `sun.nightDurationMinutes` |
|
|
364
|
+
| `dinamanaMinutes` / `ratrimanaMinutes` | `sun.dinamanaMinutes` / `sun.ratrimanaMinutes` |
|
|
365
|
+
| `siderealSunAtSunrise` | `sun.siderealLongitude` |
|
|
366
|
+
| `suryaNakshatra` | `sun.nakshatra` |
|
|
367
|
+
| `moonrise` / `moonset` | `moon.rise` / `moon.set` |
|
|
368
|
+
| `moonriseLocal` / `moonsetLocal` | `moon.riseLocal` / `moon.setLocal` |
|
|
369
|
+
| `siderealMoonAtSunrise` | `moon.siderealLongitude` |
|
|
370
|
+
| `chandraRashi` | `moon.rashi` |
|
|
371
|
+
| `tithis` / `nakshatras` / `yogas` / `karanas` / `vara` | `angas.*` (same names) |
|
|
372
|
+
| `masa` / `chandramasa` / `samvat` | `calendar.*` (same names) |
|
|
373
|
+
| `abhijitMuhurta` / `brahmaMuhurta` / `vijayaMuhurta` | `muhurtas.abhijit` / `muhurtas.brahma` / `muhurtas.vijaya` |
|
|
374
|
+
| `godhuliMuhurta` / `nishitaMuhurta` | `muhurtas.godhuli` / `muhurtas.nishita` |
|
|
375
|
+
| `amritKala` / `madhyahna` / `pratahSandhya` / `sayahnaSandhya` | `muhurtas.*` (same names) |
|
|
376
|
+
| `doGhatiMuhurta` | `muhurtas.doGhati` |
|
|
377
|
+
| `rahuKalam` / `gulikaKalam` / `yamaganda` / `durMuhurta` | `inauspicious.*` (same names) |
|
|
378
|
+
| `varjyam` / `bhadra` / `gandaMula` / `panchaka` / `panchakaRahita` | `inauspicious.*` (same names) |
|
|
379
|
+
| `choghadiya` / `hora` | `periods.choghadiya` / `periods.hora` |
|
|
380
|
+
| `gowriPanchangam` | `periods.gowri` |
|
|
381
|
+
| `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 |
|
|
382
|
+
|
|
383
|
+
Unmoved: `date`, `location`, `timezone`, `ayanamsa`, `specialYogas`,
|
|
384
|
+
`anandadiYoga`, `festivals`, `eclipse`, `chandraBalam`, `tarabala`.
|
|
385
|
+
|
|
386
|
+
For `getInstantPanchang`: `tithi` / `nakshatra` / `yoga` / `karana` / `vara` →
|
|
387
|
+
`angas.*`; `siderealSun` → `sun.siderealLongitude`; `siderealMoon` →
|
|
388
|
+
`moon.siderealLongitude`; `suryaNakshatra` → `sun.nakshatra`; `chandraRashi` →
|
|
389
|
+
`moon.rashi`; `chandramasa` / `samvat` → `calendar.*`; `panchaka` / `gandaMula`
|
|
390
|
+
→ `inauspicious.*`.
|
|
391
|
+
|
|
392
|
+
### One rule for "not applicable": always present, `null` or `[]`
|
|
393
|
+
|
|
394
|
+
4.x used three conventions and you could not predict which you would get —
|
|
395
|
+
`| null` for `bhadra` / `varjyam` / `eclipse`, `?`-optional for `chandraBalam` /
|
|
396
|
+
`tarabala`, and an empty array for `panchakaRahita` / `festivals`. v5 has one
|
|
397
|
+
rule: **every field is always present**, a value that does not apply is `null`,
|
|
398
|
+
and a collection that does not apply is `[]`.
|
|
399
|
+
|
|
400
|
+
```diff
|
|
401
|
+
- if ('chandraBalam' in r) … // 4.x: field absent without janmaRashi
|
|
402
|
+
- r.chandraBalam!.house // and the `!` was mandatory
|
|
403
|
+
+ if (r.chandraBalam !== null) … // v5: always present, null when unasked
|
|
404
|
+
+ r.chandraBalam?.house
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Only `chandraBalam` and `tarabala` changed behaviour; everything else already
|
|
408
|
+
followed the rule. `toBeUndefined()`-style checks against them become
|
|
409
|
+
`toBeNull()`.
|
|
410
|
+
|
|
411
|
+
### `suryaNakshatra` is typed as a nakshatra, not a rashi
|
|
412
|
+
|
|
413
|
+
Now published as `sun.nakshatra`. Its `index` has always been 0..26 (Ashwini …
|
|
414
|
+
Revati). Its *type* said `RashiInfo`, documented "0 = Mesha … 11 = Meena", so
|
|
415
|
+
anyone indexing a 12-element rashi array by it got silent garbage for two thirds
|
|
416
|
+
of the year. The runtime value is unchanged; the type is now
|
|
417
|
+
`NakshatraIndexInfo` and TypeScript will point at the misuse.
|
|
418
|
+
|
|
419
|
+
### `_debug` removed
|
|
420
|
+
|
|
421
|
+
`DailyPanchangResult._debug` was declared in the published type and written
|
|
422
|
+
nowhere in the library. It never carried data. If you referenced it, it was
|
|
423
|
+
always `undefined`.
|
|
424
|
+
|
|
425
|
+
### Alias fields documented rather than removed
|
|
426
|
+
|
|
427
|
+
`sun.dinamanaMinutes` / `sun.dayDurationMinutes` and `sun.ratrimanaMinutes` /
|
|
428
|
+
`sun.nightDurationMinutes` are the same numbers under classical and English
|
|
429
|
+
names. Both pairs stay — consumers use both vocabularies — and the types now say
|
|
430
|
+
plainly that they are aliases, never independently computed.
|
|
431
|
+
|
|
432
|
+
`calendar.chandramasa` keeps its casing beside `moon.rashi` and `sun.nakshatra`.
|
|
433
|
+
Renaming it would break every consumer for a casing preference. The type now
|
|
434
|
+
documents that `calendar.masa` is the **solar** month and `calendar.chandramasa`
|
|
435
|
+
the **lunar** one, which was previously left to guesswork.
|
|
436
|
+
|
|
437
|
+
### Additive, but worth knowing
|
|
438
|
+
|
|
439
|
+
- `EclipseInfo` / `EclipseSubtype` are now exported. 4.x shipped
|
|
440
|
+
`getUpcomingLunarEclipse` and friends without the type they return.
|
|
441
|
+
- `festivals[].key` — stable, language-independent festival id. Match on this,
|
|
442
|
+
never on `name`.
|
|
443
|
+
- `bhadra.locationName` — localized display name; `bhadra.location` stays the
|
|
444
|
+
machine-readable key.
|
|
445
|
+
- `MuhurtaScore.factors` — structured scoring inputs alongside English `reasons`.
|
|
446
|
+
- `BirthChart.byPlanet` — the nine placements keyed by graha.
|
|
447
|
+
- `eclipse.description` is now localized. Under `language: 'hi'` it was
|
|
448
|
+
previously emitted in English, including inside `festivals[].description`.
|
|
449
|
+
- `sections` on `getDailyPanchang` — opt into a narrower, cheaper call. See
|
|
450
|
+
[Performance](#performance).
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
69
454
|
## Features at a Glance
|
|
70
455
|
|
|
71
456
|
| Category | Features |
|
|
@@ -104,991 +489,221 @@ Ekadashi split. For reliable festival dating, use `getDailyPanchang`.
|
|
|
104
489
|
|
|
105
490
|
# Feature Reference
|
|
106
491
|
|
|
107
|
-
|
|
108
|
-
|
|
492
|
+
A quick tour with runnable snippets. **Each section links to its full page on
|
|
493
|
+
the docs site** — every option, field, and caveat lives there.
|
|
494
|
+
|
|
495
|
+
## Pancha Anga & the daily result
|
|
109
496
|
|
|
110
|
-
|
|
497
|
+
📖 [Daily Panchang →](https://dharmagya.app/docs/panchang-ts/daily-panchang)
|
|
111
498
|
|
|
112
499
|
```typescript
|
|
113
500
|
const r = getDailyPanchang(date, location, { timezone: 330 })!;
|
|
114
501
|
|
|
115
|
-
r.tithis.forEach(t => console.log(t.name, t.paksha, t.
|
|
116
|
-
r.
|
|
117
|
-
r.
|
|
118
|
-
r.
|
|
119
|
-
|
|
502
|
+
r.angas.tithis.forEach(t => console.log(t.name, t.paksha, t.endTime));
|
|
503
|
+
r.angas.vara.name; // "Mangalawara"
|
|
504
|
+
r.calendar.chandramasa.isAdhika; // true during leap months
|
|
505
|
+
r.muhurtas.brahma; // TimePeriod | null — and 9 more muhurtas
|
|
506
|
+
r.inauspicious.rahuKalam; // { start, end } — and 9 more windows
|
|
507
|
+
r.periods.choghadiya.day[0].name; // 16 Choghadiya + Gowri + 24 Hora slots
|
|
508
|
+
r.anandadiYoga.name; r.specialYogas; // Anandadi + Amrit/Sarvartha Siddhi, …
|
|
120
509
|
|
|
121
510
|
// Single-instant snapshot:
|
|
122
511
|
import { getInstantPanchang } from 'panchang-ts';
|
|
123
512
|
const i = getInstantPanchang(new Date(), location)!;
|
|
124
|
-
console.log(i.tithi.name, i.nakshatra.name
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Lunar & Solar Calendar
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
const r = getDailyPanchang(date, loc, { timezone: 330, masaSystem: 'purnimanta' })!;
|
|
131
|
-
|
|
132
|
-
r.chandramasa.name; // active system (default: Purnimanta / North Indian)
|
|
133
|
-
r.chandramasa.amantaName; // South Indian
|
|
134
|
-
r.chandramasa.purnimantaName; // North Indian
|
|
135
|
-
r.chandramasa.isAdhika; // true during leap months
|
|
136
|
-
r.samvat.vikramSamvat; // 2081
|
|
137
|
-
r.samvat.shakaSamvat; // 1946
|
|
138
|
-
|
|
139
|
-
r.masa.name; // current solar month (Mesha … Meena)
|
|
140
|
-
r.suryaNakshatra.name; // Sun's nakshatra
|
|
141
|
-
r.chandraRashi.name; // Moon sign
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## Sun, Moon & Muhurta
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
import { getSunrise, getSunset, getMoonrise, getMoonset } from 'panchang-ts';
|
|
148
|
-
|
|
149
|
-
const sunrise = getSunrise(localMidnightUtc, loc);
|
|
150
|
-
const sunset = getSunset(sunrise, loc);
|
|
151
|
-
const moonrise = getMoonrise(localMidnightUtc, loc); // null on some days (normal)
|
|
152
|
-
const moonset = getMoonset(localMidnightUtc, loc);
|
|
153
|
-
|
|
154
|
-
// Or read off the daily result:
|
|
155
|
-
const r = getDailyPanchang(date, loc, { timezone: 330 })!;
|
|
156
|
-
r.sunrise; r.sunset; r.moonrise; r.moonset; r.nextSunrise;
|
|
157
|
-
r.dayDurationMinutes; r.nightDurationMinutes;
|
|
158
|
-
|
|
159
|
-
// Auspicious muhurtas
|
|
160
|
-
r.brahmaMuhurta; // two muhurtas before sunrise
|
|
161
|
-
r.abhijitMuhurta; // 8th day-muhurta; null on Wednesday (Drik convention)
|
|
162
|
-
r.vijayaMuhurta; // 11th day-muhurta
|
|
163
|
-
r.godhuliMuhurta; // "cow-dust" sunset muhurta
|
|
164
|
-
r.nishitaMuhurta; // midnight muhurta (Shivaratri)
|
|
165
|
-
r.madhyahna; // solar noon ±24 min
|
|
166
|
-
r.pratahSandhya; // dawn twilight, ends at sunrise
|
|
167
|
-
r.sayahnaSandhya; // dusk twilight, starts at sunset
|
|
168
|
-
r.amritKala; // nakshatra-specific window (null when nakshatra has none)
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
`pratahSandhya` / `sayahnaSandhya` width = `nightDuration / 10` (~62–81 min).
|
|
172
|
-
|
|
173
|
-
## Inauspicious Periods
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
const r = getDailyPanchang(date, loc, { timezone: 330 })!;
|
|
177
|
-
|
|
178
|
-
r.rahuKalam; // { start, end }
|
|
179
|
-
r.gulikaKalam;
|
|
180
|
-
r.yamaganda;
|
|
181
|
-
r.durMuhurta; // two ~48-min windows
|
|
182
|
-
r.varjyam; // { start, end } | null
|
|
183
|
-
r.gandaMula; // { active, severity: 'mild'|'severe'|null, ... }
|
|
184
|
-
r.bhadra; // { start, end, location: 'earth'|'heaven'|'paatal', isActive } | null
|
|
185
|
-
r.panchaka; // boolean — Moon in last 5 nakshatras
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
## Time-Slot Systems
|
|
189
|
-
|
|
190
|
-
```typescript
|
|
191
|
-
const r = getDailyPanchang(date, loc, { timezone: 330 })!;
|
|
192
|
-
|
|
193
|
-
// Choghadiya — 8 day + 8 night named, rated slots (Amrit, Kaal, Shubh, Rog, …)
|
|
194
|
-
r.choghadiya.day.forEach(s => console.log(s.name, s.qualityName, s.start, s.end));
|
|
195
|
-
|
|
196
|
-
// Gowri Panchangam ("Nalla Neram") — 8 day + 8 night Tamil slots
|
|
197
|
-
r.gowriPanchangam.day.forEach(s => console.log(s.name, s.qualityName));
|
|
198
|
-
|
|
199
|
-
// Hora — 12 day + 12 night planetary hours (Chaldean order)
|
|
200
|
-
r.hora.day.forEach(h => console.log(h.planet, h.start, h.end));
|
|
201
|
-
|
|
202
|
-
// Do Ghati Muhurta — 15 day + 15 night ~48-min deity-keyed slots (no vara rotation)
|
|
203
|
-
r.doGhatiMuhurta.day.forEach(g => console.log(g.name, g.start, g.end));
|
|
204
|
-
|
|
205
|
-
// Panchaka Rahita — slices of the day FREE of Panchaka ([] when it pervades)
|
|
206
|
-
r.panchakaRahita.forEach(slice => console.log(slice.start, slice.end));
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
## Special Yogas
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
const r = getDailyPanchang(date, loc, { timezone: 330 })!;
|
|
213
|
-
|
|
214
|
-
r.anandadiYoga.name; // 28-cycle name e.g. "Ananda"
|
|
215
|
-
r.specialYogas.forEach(y => {
|
|
216
|
-
// type: amrit_siddhi | sarvartha_siddhi | ravi_pushya | guru_pushya
|
|
217
|
-
// | dwipushkar | tripushkar | jwalamukhi (inauspicious)
|
|
218
|
-
// | aadal | vidaal | ravi (Moon-from-Sun nakshatra-distance rules)
|
|
219
|
-
console.log(y.name, y.type);
|
|
220
|
-
});
|
|
513
|
+
console.log(i.angas.tithi.name, i.angas.nakshatra.name);
|
|
221
514
|
```
|
|
222
515
|
|
|
223
516
|
## Festivals (80+)
|
|
224
517
|
|
|
225
|
-
|
|
226
|
-
fast emits a `deferralDate` for Dwadashi), Pradosha, Sankranti + regional
|
|
227
|
-
variants (Pongal, Vishu, Baisakhi, Pohela Boishakh, Bihu, Uttarayan, Lohri…),
|
|
228
|
-
canonical-time classical (Janmashtami, Shivaratri, Ganesh Chaturthi, Diwali,
|
|
229
|
-
Holi, Raksha Bandhan — Bhadra-aware, Karva Chauth, Akshaya Tritiya…),
|
|
230
|
-
regional (Gudi Padwa, Gangaur, Teej variants, Onam, Chhath…), monthly
|
|
231
|
-
observances (Masik Shivaratri, Pushya days, Shravan Somvar…).
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
r.festivals.forEach(f => {
|
|
235
|
-
// type: major | minor | ekadashi | smarta_ekadashi | vaishnava_ekadashi
|
|
236
|
-
// | pradosha | sankranti | eclipse
|
|
237
|
-
console.log(f.name, f.type, f.deferralDate);
|
|
238
|
-
});
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
### Regional scoping
|
|
242
|
-
|
|
243
|
-
`region` scopes regional variants to one Indian state. Pan-Indian festivals
|
|
244
|
-
emit regardless.
|
|
518
|
+
📖 [Festivals →](https://dharmagya.app/docs/panchang-ts/festivals)
|
|
245
519
|
|
|
246
520
|
```typescript
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
// Tamil Nadu only:
|
|
252
|
-
getDailyPanchang(jan14, chennai, { timezone: 330, region: 'tamil-nadu' })!
|
|
253
|
-
.festivals.map(f => f.name);
|
|
254
|
-
// → ["Sankranti","Makar Sankranti","Pongal"]
|
|
255
|
-
|
|
256
|
-
// Lohri fires on the Hindu day BEFORE Makara transit, in Punjab/Haryana/Himachal scope:
|
|
257
|
-
getDailyPanchang(jan13, amritsar, { timezone: 330, region: 'punjab' })!
|
|
258
|
-
.festivals.some(f => f.name === 'Lohri'); // true
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
`FestivalRegion` covers 21 Indian states + `'nepal'` + `'all'` (default). The
|
|
262
|
-
legacy slugs `'tamil'`, `'bengal'`, `'north-india'` are still accepted and
|
|
263
|
-
mapped internally.
|
|
264
|
-
|
|
265
|
-
### Pre-computed table (bundled, India / IST)
|
|
521
|
+
r.festivals.forEach(f => console.log(f.key, f.name, f.type, f.deferralDate));
|
|
522
|
+
// `name` is localized, so match on `key` — never on `name`:
|
|
523
|
+
const hasDiwali = r.festivals.some(f => f.key === 'diwali');
|
|
266
524
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
5-years-future** window pre-computed against Varanasi (IST). Within India
|
|
270
|
-
these dates are essentially universal.
|
|
525
|
+
// Scope regional variants: 21 state slugs + 'nepal' + 'all' (default)
|
|
526
|
+
getDailyPanchang(jan14, chennai, { timezone: 330, region: 'tamil-nadu' });
|
|
271
527
|
|
|
272
|
-
|
|
273
|
-
import {
|
|
274
|
-
getFestivalsForYear,
|
|
275
|
-
getFestivalsForDate,
|
|
276
|
-
FESTIVALS_META,
|
|
277
|
-
FESTIVALS_YEAR_RANGE,
|
|
278
|
-
} from 'panchang-ts/festivals';
|
|
279
|
-
|
|
280
|
-
const yr = FESTIVALS_YEAR_RANGE.start; // e.g. { start: 2024, end: 2031 }
|
|
281
|
-
getFestivalsForYear(yr)!.length; // ~150 festival days
|
|
282
|
-
const diwali = getFestivalsForYear(yr)!
|
|
283
|
-
.find(d => d.festivals.some(f => f.name === 'Diwali'))!.date;
|
|
284
|
-
getFestivalsForDate(diwali); // [Narak Chaturdashi, Diwali]
|
|
285
|
-
getFestivalsForDate(diwali, 'hi'); // [नरक चतुर्दशी, दिवाली]
|
|
286
|
-
FESTIVALS_META.referenceLocation; // "Varanasi"
|
|
287
|
-
FESTIVALS_META.languages; // ["en", "hi"]
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
This entry point is engine-free — it ships only the JSON + accessors, so
|
|
291
|
-
importing it won't pull the calculation engine into your bundle. Both `en`
|
|
292
|
-
and `hi` are bundled (names *and* descriptions); pass the locale as the
|
|
293
|
-
second argument. Eclipses are excluded here (visibility is location-dependent)
|
|
294
|
-
— they ship as their own bundled table at `panchang-ts/eclipses` (see
|
|
295
|
-
[Eclipses](#eclipses)).
|
|
296
|
-
|
|
297
|
-
### Festivals outside India — build a location table and cache it
|
|
298
|
-
|
|
299
|
-
The bundled table is **IST-only**. Elsewhere (Europe, North America, rest
|
|
300
|
-
of world) festival dates can shift by ±1 day, because canonical times
|
|
301
|
-
(nishita / pradosha / chandrodaya …) are observer-dependent — and the shift
|
|
302
|
-
tracks the timezone offset, not the "region", so a single per-continent
|
|
303
|
-
table would mis-date boundary-day festivals.
|
|
304
|
-
|
|
305
|
-
For an offline app serving users worldwide, the right pattern is
|
|
306
|
-
**compute-once-then-cache for the user's actual location**. Build a
|
|
307
|
-
location-specific table with `buildFestivalsTable` (from the main entry —
|
|
308
|
-
it uses the engine), persist the returned JSON, then read it back through
|
|
309
|
-
the same accessors via their `source` argument:
|
|
310
|
-
|
|
311
|
-
```typescript
|
|
528
|
+
// Pre-computed table: build once, cache the JSON, read engine-free.
|
|
312
529
|
import { buildFestivalsTable } from 'panchang-ts';
|
|
313
|
-
import {
|
|
314
|
-
|
|
315
|
-
// On first use at the user's location (a few seconds on-device — run it in
|
|
316
|
-
// the background / chunk by year), then cache `table` to disk/MMKV.
|
|
317
|
-
const table = buildFestivalsTable({
|
|
318
|
-
location: { latitude: 40.7128, longitude: -74.006 },
|
|
319
|
-
timezoneOffsetMinutes: -300, // US Eastern (EST); 0 = UK, 330 = IST
|
|
320
|
-
startYear: 2024,
|
|
321
|
-
endYear: 2031,
|
|
322
|
-
languages: ['en'], // omit hi to halve the size
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
// Later reads are instant lookups against the cached table:
|
|
326
|
-
getFestivalsForYear(2026, 'en', table);
|
|
327
|
-
getFestivalsForDate('2026-11-08', 'en', table); // key is in the table's tz
|
|
530
|
+
import { readFestivalsForYear, readFestivalsForDate } from 'panchang-ts/festivals';
|
|
328
531
|
```
|
|
329
532
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
zero first-load latency and only compute-and-cache for non-IST users.
|
|
533
|
+
No table ships with the package — festival dates are observer-dependent, so you
|
|
534
|
+
build one for your users' location and years (`npm run festivals:gen` is a
|
|
535
|
+
worked example).
|
|
334
536
|
|
|
335
|
-
|
|
336
|
-
paksha naming. To regenerate the bundled India table after a registry
|
|
337
|
-
change, run `npm run festivals:gen` (rolling window, no constants to edit).
|
|
537
|
+
## Eclipses & Moon Phases
|
|
338
538
|
|
|
339
|
-
|
|
539
|
+
📖 [Eclipses & Moon Phases →](https://dharmagya.app/docs/panchang-ts/eclipses-moon-phases)
|
|
340
540
|
|
|
341
541
|
```typescript
|
|
342
|
-
const r = getDailyPanchang(date, loc, { timezone: 330 })!;
|
|
343
542
|
if (r.eclipse) {
|
|
344
|
-
r.eclipse.kind;
|
|
345
|
-
r.eclipse.
|
|
346
|
-
r.eclipse.magnitude;
|
|
347
|
-
r.eclipse.visibleFromLocation; // body above horizon at peak?
|
|
348
|
-
r.eclipse.start; r.eclipse.peak; r.eclipse.end;
|
|
543
|
+
r.eclipse.kind; r.eclipse.subtype; // 'solar'|'lunar', 'partial'|'total'|…
|
|
544
|
+
r.eclipse.obscuration; // disc AREA covered, 0..1
|
|
545
|
+
r.eclipse.magnitude; // catalogue DIAMETER fraction
|
|
349
546
|
r.eclipse.sutakStart; r.eclipse.sutakEnd;
|
|
350
|
-
// Sutak: 12 h (4 prahara) before solar, 9 h (3 prahara) before lunar
|
|
351
547
|
}
|
|
352
548
|
|
|
353
|
-
import { getUpcomingSolarEclipse,
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
### Pre-computed table (bundled, India / IST)
|
|
358
|
-
|
|
359
|
-
Like the festivals table, eclipse data ships as a static, engine-free entry
|
|
360
|
-
at `panchang-ts/eclipses` — a rolling **2-years-past / 5-years-future** window
|
|
361
|
-
pre-computed against Varanasi (IST). It lists every eclipse **visible from
|
|
362
|
-
there during any phase** (so an eclipse already in progress at moon/sunrise or
|
|
363
|
-
moon/sunset is included); the `visibleAtPeak` flag tells you whether greatest
|
|
364
|
-
eclipse itself is observable. Within India visibility is essentially uniform.
|
|
549
|
+
import { getUpcomingSolarEclipse, computeMoonPhasesInRange } from 'panchang-ts';
|
|
550
|
+
getUpcomingSolarEclipse(new Date(), loc, 365);
|
|
551
|
+
computeMoonPhasesInRange(start, end); // precise new/quarter/full instants
|
|
365
552
|
|
|
366
|
-
|
|
367
|
-
import {
|
|
368
|
-
getEclipsesForYear,
|
|
369
|
-
getEclipsesForDate,
|
|
370
|
-
ECLIPSES_META,
|
|
371
|
-
ECLIPSES_YEAR_RANGE,
|
|
372
|
-
} from 'panchang-ts/eclipses';
|
|
373
|
-
|
|
374
|
-
const e = getEclipsesForYear(2025)![0].eclipses[0];
|
|
375
|
-
e.kind; // 'lunar'
|
|
376
|
-
e.subtype; // 'total'
|
|
377
|
-
e.start; e.peak; e.end; // ISO UTC strings
|
|
378
|
-
e.magnitude; // 0..1 obscuration at peak
|
|
379
|
-
e.visibleFromLocation; // visible during any phase? (always true in bundled table)
|
|
380
|
-
e.visibleAtPeak; // is greatest eclipse itself above the horizon?
|
|
381
|
-
e.sutak; // { start, end } — see note below
|
|
382
|
-
getEclipsesForDate('2025-09-07', 'hi'); // [पूर्ण चंद्र ग्रहण]
|
|
383
|
-
ECLIPSES_META.referenceLocation; // "Varanasi"
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
Each entry carries `en` + `hi` text. Solar eclipses report the subtype seen
|
|
387
|
-
**locally** (a globally-total eclipse may read `partial` from Varanasi). The
|
|
388
|
-
`sutak` window is present only where it applies — all visible solar eclipses
|
|
389
|
-
and visible **umbral** (partial/total) lunar eclipses; **penumbral** lunar
|
|
390
|
-
eclipses carry no `sutak` and are not religiously observed (drik / pandit
|
|
391
|
-
consensus). For full astronomical detail (e.g. eclipses *not* visible in
|
|
392
|
-
India), use `getUpcomingEclipses` / `getEclipsesInRange` from the main entry.
|
|
393
|
-
|
|
394
|
-
**Outside India:** which eclipses are visible — and thus carry `sutak` —
|
|
395
|
-
differs by location. Build and cache a location-specific table with
|
|
396
|
-
`buildEclipsesTable` (main entry, uses the engine), then read it back via the
|
|
397
|
-
same accessors' `source` argument — the same compute-once-then-cache pattern
|
|
398
|
-
as festivals:
|
|
399
|
-
|
|
400
|
-
```typescript
|
|
401
|
-
import { buildEclipsesTable } from 'panchang-ts';
|
|
402
|
-
import { getEclipsesForYear } from 'panchang-ts/eclipses';
|
|
403
|
-
|
|
404
|
-
const table = buildEclipsesTable({
|
|
405
|
-
location: { latitude: 51.5074, longitude: -0.1278 },
|
|
406
|
-
timezoneOffsetMinutes: 0, // UK / GMT
|
|
407
|
-
startYear: 2024,
|
|
408
|
-
endYear: 2031,
|
|
409
|
-
// visibleOnly: false → also include eclipses below the horizon (no sutak)
|
|
410
|
-
});
|
|
411
|
-
getEclipsesForYear(2025, 'en', table);
|
|
553
|
+
// Engine-free tables: panchang-ts/eclipses and panchang-ts/moon-phases
|
|
412
554
|
```
|
|
413
555
|
|
|
414
|
-
|
|
415
|
-
window, no constants to edit).
|
|
416
|
-
|
|
417
|
-
## Moon Phases
|
|
556
|
+
## Muhurta Engine
|
|
418
557
|
|
|
419
|
-
|
|
420
|
-
**full** (Purnima), **last quarter** — as precise instants. (These are the
|
|
421
|
-
astronomical quarter moments, distinct from the same-named *tithis*, which are
|
|
422
|
-
~24h windows.)
|
|
558
|
+
📖 [Muhurta Engine →](https://dharmagya.app/docs/panchang-ts/muhurta)
|
|
423
559
|
|
|
424
560
|
```typescript
|
|
425
|
-
import {
|
|
426
|
-
const phases = getMoonPhasesInRange(new Date('2026-01-01'), new Date('2026-12-31'));
|
|
427
|
-
phases.forEach(p => console.log(p.phase, p.time.toISOString())); // ~49 / year
|
|
428
|
-
```
|
|
561
|
+
import { scoreMuhurta, computeAuspiciousDatesInRange, vivahRule } from 'panchang-ts';
|
|
429
562
|
|
|
430
|
-
|
|
563
|
+
const s = scoreMuhurta(new Date('2026-05-12'), DELHI, vivahRule, { timezone: 330 });
|
|
564
|
+
s.score; // 0..100; passes when ≥ 50
|
|
565
|
+
s.factors; // structured, stable codes — localize/filter on these
|
|
566
|
+
s.reasons; // diagnostic English
|
|
431
567
|
|
|
432
|
-
|
|
433
|
-
— a rolling **2-years-past / 5-years-future** window. Phases are global
|
|
434
|
-
instants; the bundled table maps each onto its **IST** calendar date (so a new
|
|
435
|
-
moon at 19:52 UTC on Jan 18 is listed under Jan 19 in India).
|
|
436
|
-
|
|
437
|
-
```typescript
|
|
438
|
-
import {
|
|
439
|
-
getMoonPhasesForYear,
|
|
440
|
-
getMoonPhasesForDate,
|
|
441
|
-
MOON_PHASES_META,
|
|
442
|
-
MOON_PHASES_YEAR_RANGE,
|
|
443
|
-
} from 'panchang-ts/moon-phases';
|
|
444
|
-
|
|
445
|
-
getMoonPhasesForYear(2026)!.length; // ~49 phase days
|
|
446
|
-
getMoonPhasesForDate('2026-01-03'); // [{ phase: 'full', name: 'Full Moon', ... }]
|
|
447
|
-
getMoonPhasesForDate('2026-01-03', 'hi'); // [{ phase: 'full', name: 'पूर्णिमा', ... }]
|
|
568
|
+
computeAuspiciousDatesInRange(vivahRule, start, end, DELHI, { timezone: 330 });
|
|
448
569
|
```
|
|
449
570
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
`
|
|
571
|
+
13 stock rules (vivah, griha pravesh, namakarana, …) or your own pure-data
|
|
572
|
+
`MuhurtaRule`. Vara × Tithi yogas (Siddha, Amrita, Dagdha, …) are scored
|
|
573
|
+
jointly. Pre-compute a table with `buildMuhurtaTable` and read it back through
|
|
574
|
+
`panchang-ts/muhurta` (~1.7 KB, no astronomy code).
|
|
454
575
|
|
|
455
|
-
|
|
456
|
-
import { buildMoonPhasesTable } from 'panchang-ts';
|
|
457
|
-
import { getMoonPhasesForYear } from 'panchang-ts/moon-phases';
|
|
458
|
-
|
|
459
|
-
const table = buildMoonPhasesTable({
|
|
460
|
-
timezoneOffsetMinutes: -300, // US Eastern
|
|
461
|
-
startYear: 2024, endYear: 2031,
|
|
462
|
-
});
|
|
463
|
-
getMoonPhasesForYear(2026, 'en', table);
|
|
464
|
-
```
|
|
576
|
+
## Planetary Positions & Birth Charts
|
|
465
577
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
## Planetary Positions
|
|
578
|
+
📖 [Birth Charts →](https://dharmagya.app/docs/panchang-ts/birth-chart)
|
|
469
579
|
|
|
470
580
|
```typescript
|
|
471
|
-
import {
|
|
581
|
+
import {
|
|
582
|
+
computePlanetaryPositions, computeLagna, computeBhava,
|
|
583
|
+
computeRashiChart, computeNavamsa, computeDivisionalChart, computeDignity,
|
|
584
|
+
} from 'panchang-ts';
|
|
472
585
|
|
|
473
586
|
const g = computePlanetaryPositions(new Date(), 'lahiri');
|
|
474
|
-
g.jupiter.rashi.name;
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
// True node (sharper Rahu/Ketu via Meeus periodic correction)
|
|
482
|
-
const gT = computePlanetaryPositions(new Date(), 'lahiri', undefined, 'true');
|
|
587
|
+
g.jupiter.rashi.name; g.jupiter.nakshatra.pada; g.saturn.isRetrograde;
|
|
588
|
+
|
|
589
|
+
const d1 = computeRashiChart(birth, loc); // houseSystem: whole-sign | equal | placidus-kp
|
|
590
|
+
d1.byPlanet.Mars.house; // keyed lookup, no linear scan
|
|
591
|
+
const d9 = computeNavamsa(birth, loc); // + D2/D3/D7/D10/D12/D30
|
|
592
|
+
computeDignity('Mars', 9); // 'exalted'
|
|
483
593
|
```
|
|
484
594
|
|
|
485
|
-
## Dashas
|
|
595
|
+
## Dashas & Personal Transits
|
|
486
596
|
|
|
487
|
-
|
|
597
|
+
📖 [Dashas & Transits →](https://dharmagya.app/docs/panchang-ts/dashas)
|
|
488
598
|
|
|
489
599
|
```typescript
|
|
490
600
|
import {
|
|
491
601
|
computeVimshottariDashaFromBirth, computeVimshottariPratyantar,
|
|
492
|
-
computeAshtottariDasha, computeYoginiDasha, computeCharaDasha,
|
|
602
|
+
computeAshtottariDasha, computeYoginiDasha, computeCharaDasha,
|
|
603
|
+
computeNarayanDasha, computeSadeSati,
|
|
493
604
|
} from 'panchang-ts';
|
|
494
605
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
// 2. Ashtottari — 108-year, 8-lord cycle (no Ketu).
|
|
500
|
-
const ash = computeAshtottariDasha(birth, moonLon);
|
|
501
|
-
|
|
502
|
-
// 3. Yogini — 36-year, 8 yoginis.
|
|
503
|
-
const yog = computeYoginiDasha(birth, moonLon);
|
|
504
|
-
yog.mahaDashas[0]!.yogini; // 'Dhanya'
|
|
505
|
-
yog.mahaDashas[0]!.lord; // 'Jupiter'
|
|
506
|
-
|
|
507
|
-
// 4. Chara (Jaimini) — sign-based, 9-8-7 years per modality, forward only.
|
|
508
|
-
const cha = computeCharaDasha(birth, loc);
|
|
509
|
-
|
|
510
|
-
// 5. Narayan (Jaimini) — sign-based, parity-based direction.
|
|
511
|
-
// Vishama-pada lagna {Aries, Taurus, Gemini, Libra, Scorpio, Sag} → forward
|
|
512
|
-
// Sama-pada lagna {Cancer, Leo, Virgo, Capricorn, Aquarius, Pisces} → backward
|
|
513
|
-
const nar = computeNarayanDasha(birth, loc);
|
|
514
|
-
nar.direction; // 'forward' | 'backward'
|
|
515
|
-
|
|
516
|
-
// Narayan variable-duration variant (Sanjay Rath):
|
|
517
|
-
const narV = computeNarayanDasha(birth, loc, 'lahiri', { duration: 'variable' });
|
|
518
|
-
narV.mahaDashas[0]!.years; // 0..12 from rashi-to-lord count (+1 exalt, -1 debil)
|
|
606
|
+
const vim = computeVimshottariDashaFromBirth(birth, 'lahiri'); // 3-level
|
|
607
|
+
computeSadeSati(natalMoonRashiIndex, new Date());
|
|
608
|
+
// Daily transits: pass janmaRashi / janmaNakshatra to getDailyPanchang
|
|
609
|
+
// and read r.chandraBalam / r.tarabala.
|
|
519
610
|
```
|
|
520
611
|
|
|
521
|
-
##
|
|
612
|
+
## Strength, Yogas & Karakas
|
|
522
613
|
|
|
523
|
-
|
|
524
|
-
const r = getDailyPanchang(date, loc, {
|
|
525
|
-
timezone: 330,
|
|
526
|
-
janmaRashi: 3, // 0 = Mesha … 11 = Meena
|
|
527
|
-
janmaNakshatra: 0, // 0 = Ashwini … 26 = Revati
|
|
528
|
-
})!;
|
|
529
|
-
r.chandraBalam!; // { house, quality: 'strong'|'weak', name, englishName }
|
|
530
|
-
r.tarabala!; // { taraIndex, name, englishName, quality }
|
|
531
|
-
|
|
532
|
-
import { computeSadeSati } from 'panchang-ts';
|
|
533
|
-
const ss = computeSadeSati(natalMoonRashiIndex, new Date());
|
|
534
|
-
// → { active, phase: 1|2|3|null, currentArcStart, currentArcEnd, nextArcStart }
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
## Birth Chart
|
|
538
|
-
|
|
539
|
-
Sidereal Lagna, Bhava under three house systems, D1 + six classical divisional
|
|
540
|
-
charts (D2/D3/D7/D9/D10/D12/D30), and Planetary Dignity.
|
|
614
|
+
📖 [Strength, Yogas & Karakas →](https://dharmagya.app/docs/panchang-ts/strength-yogas)
|
|
541
615
|
|
|
542
616
|
```typescript
|
|
543
617
|
import {
|
|
544
|
-
|
|
545
|
-
|
|
618
|
+
computeAspects, computeShadbala, computeBhavaBala,
|
|
619
|
+
computeAshtakavarga, computeYogas, computeJaiminiKarakas,
|
|
546
620
|
} from 'panchang-ts';
|
|
547
621
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
// Bhava — 'whole-sign' (default) | 'equal' | 'placidus-kp'.
|
|
554
|
-
// Placidus-KP throws PanchangError('CIRCUMPOLAR') beyond ±66.5°.
|
|
555
|
-
const houses = computeBhava(birth, loc, { houseSystem: 'whole-sign' });
|
|
556
|
-
|
|
557
|
-
// D1 — full Rashi chart with 9-graha house placement.
|
|
558
|
-
const d1 = computeRashiChart(birth, loc, { houseSystem: 'whole-sign' });
|
|
559
|
-
d1.planets.find(p => p.planet === 'Jupiter')?.house;
|
|
560
|
-
d1.planets.find(p => p.planet === 'Saturn')?.isRetrograde;
|
|
561
|
-
|
|
562
|
-
// Divisional charts (D2 Hora, D3 Drekkana, D7 Saptamsa, D9 Navamsa,
|
|
563
|
-
// D10 Dasamsa, D12 Dwadasamsa, D30 Trimsamsa).
|
|
564
|
-
const d9 = computeNavamsa(birth, loc);
|
|
565
|
-
const d10 = computeDivisionalChart(birth, loc, 'D10');
|
|
566
|
-
const d30 = computeDivisionalChart(birth, loc, 'D30');
|
|
567
|
-
|
|
568
|
-
// Planetary dignity (BPHS Ch.3-4).
|
|
569
|
-
computeDignity('Mars', 0); // 'moolatrikona' (Aries)
|
|
570
|
-
computeDignity('Mars', 9); // 'exalted' (Capricorn)
|
|
571
|
-
computeDignity('Sun', 6); // 'debilitated' (Libra)
|
|
622
|
+
computeShadbala(birth, loc); // 6-fold, in Virupas
|
|
623
|
+
computeAshtakavarga(d1, { reductions: true }); // Bhinna + Sarva + Sodhana
|
|
624
|
+
computeYogas(d1); // ~25 named, with bhanga
|
|
625
|
+
computeJaiminiKarakas(d1, { variant: '8-jaimini' });
|
|
572
626
|
```
|
|
573
627
|
|
|
574
|
-
Birth-chart helpers accept the full ayanamsa set including `'true-chitra'` and
|
|
575
|
-
`'thirukanitham'`.
|
|
576
|
-
|
|
577
628
|
## Compatibility & Doshas
|
|
578
629
|
|
|
630
|
+
📖 [Matching & Doshas →](https://dharmagya.app/docs/panchang-ts/matching-doshas)
|
|
631
|
+
|
|
579
632
|
```typescript
|
|
580
633
|
import {
|
|
581
634
|
computeAshtakoot, computePathuPorutham,
|
|
582
|
-
computeMangalDosha, computeKaalSarp, computePitruDosha,
|
|
635
|
+
computeMangalDosha, computeMangalCompatibility, computeKaalSarp, computePitruDosha,
|
|
583
636
|
} from 'panchang-ts';
|
|
584
637
|
|
|
585
|
-
|
|
586
|
-
// Graha Maitri, Gana, Bhakoot, Nadi (max 1/2/3/4/5/6/7/8).
|
|
587
|
-
const match = computeAshtakoot(
|
|
588
|
-
{ rashi: 4, nakshatra: 9 },
|
|
589
|
-
{ rashi: 0, nakshatra: 1 },
|
|
590
|
-
);
|
|
638
|
+
computeAshtakoot({ rashi: 4, nakshatra: 9 }, { rashi: 0, nakshatra: 1 });
|
|
591
639
|
// → { totalScore: 0..36, koots: KootScore[8], cancellations: string[] }
|
|
592
640
|
|
|
593
|
-
//
|
|
594
|
-
//
|
|
595
|
-
// `navamsaRashi` enables the same-Navamsa-lord rule.
|
|
596
|
-
const richer = computeAshtakoot(
|
|
597
|
-
{ rashi: 4, nakshatra: 9, lagnaRashi: 7, navamsaRashi: 2 },
|
|
598
|
-
{ rashi: 0, nakshatra: 1, lagnaRashi: 1, navamsaRashi: 5 },
|
|
599
|
-
);
|
|
600
|
-
|
|
601
|
-
// Pathu Porutham (Tamil/Kerala, 10-fold) — binary pass/fail per koot.
|
|
602
|
-
// Three vetoes (Yoni, Rajju, Vedha) flip `recommended` regardless of count.
|
|
603
|
-
const tp = computePathuPorutham(
|
|
604
|
-
{ rashi: 4, nakshatra: 9 },
|
|
605
|
-
{ rashi: 0, nakshatra: 1 },
|
|
606
|
-
);
|
|
607
|
-
tp.totalPasses; // 0..10
|
|
608
|
-
tp.recommended; // no veto + ≥5 passes
|
|
609
|
-
|
|
610
|
-
// Doshas
|
|
611
|
-
computeMangalDosha(d1);
|
|
612
|
-
// Mars in 1/2/4/7/8/12 from Lagna, Moon, AND Venus (Drik rule set).
|
|
613
|
-
// Cancellations: Mars in own sign/exalted, conjunct Jup/Moon/Venus,
|
|
614
|
-
// or aspected by Jupiter (5/7/9 sign-aspect).
|
|
615
|
-
// Severity (anshik/purna) is computed pre-cancellation.
|
|
616
|
-
|
|
617
|
-
computeKaalSarp(d1);
|
|
618
|
-
// 12 subtypes by Rahu's house: anant, kulik, vasuki, shankhpal, padma,
|
|
619
|
-
// mahapadma, takshak, karkotak, shankhachud, ghatak, vishdhar, sheshnag.
|
|
620
|
-
|
|
621
|
-
computePitruDosha(d1);
|
|
622
|
-
// Pandit-consensus 4-trigger set (rules cited by ≥3 of 6 surveyed
|
|
623
|
-
// pandit sources): Sun+Rahu conjunction (any house), Sun+Saturn
|
|
624
|
-
// conjunction (any house), Rahu in 9th house, 9th-lord conjunct Rahu.
|
|
625
|
-
// Drik panchang publishes no Pitru calculator; minority/expansive
|
|
626
|
-
// rules (Sun in 9th alone, Ketu in 4th, 9th lord in dusthana, etc.)
|
|
627
|
-
// are intentionally excluded.
|
|
641
|
+
computeMangalCompatibility(boyChart, girlChart); // Manglik is a PAIRWISE verdict
|
|
642
|
+
computeKaalSarp(d1); // 12 subtypes by Rahu's house
|
|
628
643
|
```
|
|
629
644
|
|
|
630
|
-
|
|
631
|
-
Bhakoot Parivartana (rashi-lord exchange) cancellation needs per-graha
|
|
632
|
-
position data not carried by the `NatalMoon` shape and is not modelled.
|
|
645
|
+
## Annual Charts, Sensitive Points, KP & Prashna
|
|
633
646
|
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
```typescript
|
|
637
|
-
import {
|
|
638
|
-
computeAspects, computeShadbala, computeBhavaBala, computeAshtakavarga,
|
|
639
|
-
} from 'panchang-ts';
|
|
640
|
-
|
|
641
|
-
// Drishti — every graha aspects the 7th; malefics gain extras
|
|
642
|
-
// (Mars 4+8, Jupiter 5+9, Saturn 3+10). Node aspect mode is configurable:
|
|
643
|
-
const aspects = computeAspects(d1); // BPHS 7th-only on nodes
|
|
644
|
-
const aspExt = computeAspects(d1, { nodeAspects: '5-and-9' }); // KP/BV Raman extension
|
|
645
|
-
|
|
646
|
-
// Shadbala — 7 visible grahas, 6 components, in Virupas (60 V = 1 Rupa).
|
|
647
|
-
// Sthana = Uchcha + Saptavargaja (D1/D2/D3/D7/D9/D12/D30 dignity sum)
|
|
648
|
-
// + Ojha-Yugma (rashi+navamsa parity) + Drekkana (gender decanate).
|
|
649
|
-
// Range [0, 420 V]. Dig is directional cusp; Kala = Nathonatha + Paksha;
|
|
650
|
-
// Chesta is retrograde-bucket; Naisargika is fixed rank; Drik is weighted aspects.
|
|
651
|
-
const bala = computeShadbala(birth, loc);
|
|
652
|
-
|
|
653
|
-
// Bhava Bala — 12-bhava strength built on top of Shadbala.
|
|
654
|
-
// Per-bhava: { bhavadhipati, dik, drik, sthana, total }.
|
|
655
|
-
const bhavaBala = computeBhavaBala(birth, loc);
|
|
656
|
-
|
|
657
|
-
// Ashtakavarga — 12-rashi bindu grids (BPHS Ch. 66).
|
|
658
|
-
const av = computeAshtakavarga(d1);
|
|
659
|
-
av.sarvashtaka; // 12 cells, each 0..56, total 336
|
|
660
|
-
av.bhinnashtaka.Jupiter; // 12-cell grid; Jupiter total = 56 (chart-invariant)
|
|
661
|
-
// Other invariants: Sun=47, Moon=49, Mars=39, Mercury=54, Venus=52, Saturn=39.
|
|
662
|
-
|
|
663
|
-
// Trikona + Ekadhipatya Sodhana reductions (BPHS Ch. 67):
|
|
664
|
-
const avR = computeAshtakavarga(d1, { reductions: true });
|
|
665
|
-
avR.reduced!.sarvashtaka;
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
Rahu and Ketu are not Ashtakavarga receivers or contributors (classical
|
|
669
|
-
Parashara scheme).
|
|
670
|
-
|
|
671
|
-
## Yogas & Karakas
|
|
672
|
-
|
|
673
|
-
```typescript
|
|
674
|
-
import { computeYogas, computeJaiminiKarakas } from 'panchang-ts';
|
|
675
|
-
|
|
676
|
-
// ~25 named yogas — Pancha Mahapurusha (Ruchaka/Bhadra/Hamsa/Malavya/Sasha),
|
|
677
|
-
// lunar (Gajakesari, Sunapha, Anapha, Durudhura, Kemadruma), solar
|
|
678
|
-
// (Budha-Aditya, Veshi, Vasi, Ubhayachari), Raja (kendra/trikona-lord,
|
|
679
|
-
// Dharma-Karmadhipati, Vipareeta, Lakshmi), Dhana (2-11, 5-9, Vasumati),
|
|
680
|
-
// Vargottama, Yogakaraka, Neecha Bhanga, Daridra.
|
|
681
|
-
const yogas = computeYogas(d1);
|
|
682
|
-
// → [{ name, type, reasons[], bhanga?: { applies, reasons[] } }, …]
|
|
683
|
-
|
|
684
|
-
// Optional cancellation annotations: 5 Pancha Mahapurusha + Gajakesari
|
|
685
|
-
// surface `bhanga` (Sun/Moon conjunct or Jupiter combust/debilitated).
|
|
686
|
-
// Neecha Bhanga: dispositor in kendra from Lagna OR Moon; lord-of-
|
|
687
|
-
// exaltation-rashi in kendra from Lagna or Moon; mutual exchange;
|
|
688
|
-
// dispositor aspecting the debilitated planet.
|
|
689
|
-
|
|
690
|
-
// Filter by type / pass D9 for Vargottama:
|
|
691
|
-
const d9 = computeNavamsa(birth, loc);
|
|
692
|
-
const all = computeYogas(d1, { types: ['raja','dhana'], navamsa: d9 });
|
|
693
|
-
|
|
694
|
-
// Jaimini Karakas — Atmakaraka (highest degree-in-rashi) … Darakaraka (lowest).
|
|
695
|
-
const k7 = computeJaiminiKarakas(d1); // 7-graha Parashara default
|
|
696
|
-
const k8 = computeJaiminiKarakas(d1, { variant: '8-jaimini' }); // adds Rahu (degree reversed),
|
|
697
|
-
// inserts Pitrukaraka at 5th
|
|
698
|
-
```
|
|
699
|
-
|
|
700
|
-
Yoga and Karaka names are English/transliterated proper nouns and intentionally
|
|
701
|
-
**not** locale-resolved.
|
|
702
|
-
|
|
703
|
-
## Annual & Sensitive Layers
|
|
647
|
+
📖 [Annual Charts →](https://dharmagya.app/docs/panchang-ts/annual-charts) ·
|
|
648
|
+
[KP & Prashna →](https://dharmagya.app/docs/panchang-ts/kp-prashna)
|
|
704
649
|
|
|
705
650
|
```typescript
|
|
706
651
|
import {
|
|
707
652
|
computeVarshaphala, computeTithiPravesha, computeArudhas,
|
|
708
653
|
computeHoraLagna, computeGhatiLagna, computeBhavaLagna, computeSripatiLagna,
|
|
709
654
|
computeUpagrahas, computeArgala,
|
|
710
|
-
} from 'panchang-ts';
|
|
711
|
-
|
|
712
|
-
// Varshaphala — Tajik annual chart for the Nth solar return.
|
|
713
|
-
const v = computeVarshaphala(birth, 30, loc);
|
|
714
|
-
v.solarReturnInstant;
|
|
715
|
-
v.varshaLagna.rashi.name;
|
|
716
|
-
v.muntha.rashi; v.muntha.house; // muntha = (natalLagnaRashi + 30) mod 12
|
|
717
|
-
v.yearLord; // strongest of 4 candidates by Shadbala
|
|
718
|
-
v.sahams.Punya.house;
|
|
719
|
-
v.sahams.Vivaha.rashi;
|
|
720
|
-
// 27 Sahams: Punya, Vidya, Yasas, Mitra, Karma, Vivaha, Putra, Roga, Marana,
|
|
721
|
-
// Rajya, Raja, Bandhu, Dharma, Gnati, Apamrityu, Bhratri, Matri, Pitri, Sama,
|
|
722
|
-
// Bandhana, Karyasiddhi, Vyapara, Sastra, Asha, Labha, Susha, Tapas.
|
|
723
|
-
|
|
724
|
-
// Tithi Pravesha — annual chart cast when Sun is in natal sidereal sign AND
|
|
725
|
-
// Sun-Moon separation equals natal separation. Preserves natal tithi exactly.
|
|
726
|
-
const tp = computeTithiPravesha(birth, 30, loc);
|
|
727
|
-
tp.natalTithi === tp.praveshTithi; // always true
|
|
728
|
-
|
|
729
|
-
// Arudha padas — image/reflection of each bhava. Arudha[0] = Arudha Lagna (AL).
|
|
730
|
-
const a = computeArudhas(d1);
|
|
731
|
-
a[0]!.bhava; // 1 — AL
|
|
732
|
-
a[0]!.arudhaRashi; // 0..11
|
|
733
|
-
a[6]!.bhava; // 7 — Darapada (spouse pada)
|
|
734
|
-
|
|
735
|
-
// Special lagnas — time-derived sensitive points from sunrise on/before birth.
|
|
736
|
-
computeHoraLagna(birth, loc); // 30°/hour (1 rashi/hour)
|
|
737
|
-
computeGhatiLagna(birth, loc); // 75°/hour (1 rashi/24 min)
|
|
738
|
-
computeBhavaLagna(birth, loc); // 15°/hour (1 rashi/2 hours)
|
|
739
|
-
computeSripatiLagna(birth, loc); // = natal lagna (cusp 1)
|
|
740
|
-
|
|
741
|
-
// Sripati cusps 2–12 (opt-in) — 4 angular cusps + trisected intermediates.
|
|
742
|
-
// Defined at every latitude (unlike Placidus).
|
|
743
|
-
const sripati = computeSripatiLagna(birth, loc, 'lahiri', 'en', { includeCusps: true });
|
|
744
|
-
sripati.cusps; // number[12] of bhava madhyas; cusps[0/3/6/9] = ASC/IC/DSC/MC
|
|
745
|
-
|
|
746
|
-
// Upagrahas — Gulika, Mandi (rising-asc at Saturn segment start/midpoint),
|
|
747
|
-
// plus Sun-derived Dhuma, Vyatipata, Parivesha, Indrachapa, Upaketu.
|
|
748
|
-
const u = computeUpagrahas(birth, loc);
|
|
749
|
-
u.gulika.longitude; u.gulika.rashi; u.gulika.house;
|
|
750
|
-
|
|
751
|
-
// Argala (Jaimini) — planets in 2/4/11 from a bhava form Argala (intervention);
|
|
752
|
-
// 3/10/12 form Virodhargala (counter). Each planet hits exactly 6 of 12 bhavas.
|
|
753
|
-
const arg = computeArgala(d1);
|
|
754
|
-
arg[0]!.argala; arg[0]!.virodhargala;
|
|
755
|
-
|
|
756
|
-
// Trikonargala (5/9 trine, opt-in) — Ketu reversal: 5th-from → virodhaka,
|
|
757
|
-
// 9th-from → source.
|
|
758
|
-
const argT = computeArgala(d1, { includeTrikonargala: true });
|
|
759
|
-
argT[0]!.trikona!.sources;
|
|
760
|
-
argT[0]!.trikona!.virodhakas;
|
|
761
|
-
```
|
|
762
|
-
|
|
763
|
-
## KP & Prashna
|
|
764
|
-
|
|
765
|
-
```typescript
|
|
766
|
-
import {
|
|
767
655
|
computeKpSubLord, computeKpCuspalSubLords, computeKpSignificators,
|
|
768
656
|
computePrashnaChart,
|
|
769
657
|
} from 'panchang-ts';
|
|
770
658
|
|
|
771
|
-
|
|
772
|
-
//
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
info.starLord; // 'Moon'
|
|
776
|
-
info.subLord;
|
|
777
|
-
|
|
778
|
-
// Cuspal sub-lords (always Placidus-KP — KP's anchor scheme).
|
|
779
|
-
const cusps = computeKpCuspalSubLords(birth, loc);
|
|
780
|
-
cusps.cusps[0]!.subLord; // ascendant
|
|
781
|
-
cusps.cusps[6]!.subLord; // descendant
|
|
782
|
-
|
|
783
|
-
// Significators — for each planet, the houses it signifies via the 4-fold KP rule
|
|
784
|
-
// (occupant + star-lord-occupant + owner + star-lord-owner).
|
|
785
|
-
const sig = computeKpSignificators(d1);
|
|
786
|
-
sig.byPlanet.Sun;
|
|
787
|
-
sig.byHouse[10];
|
|
788
|
-
|
|
789
|
-
// Prashna (horary) chart — cast at question moment from querent's location.
|
|
790
|
-
const pchart = computePrashnaChart(
|
|
791
|
-
new Date('2026-05-09T14:30:00Z'),
|
|
792
|
-
{ latitude: 19.0760, longitude: 72.8777 },
|
|
793
|
-
);
|
|
794
|
-
pchart.lagna.rashi.name;
|
|
795
|
-
pchart.bhava.system; // 'placidus-kp' by default (KP horary anchor)
|
|
796
|
-
pchart.planets[1]!.house; // Moon — primary mind significator
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
Same return shape as a natal `BirthChart`. Pass `{ houseSystem: 'whole-sign' }`
|
|
800
|
-
to `computePrashnaChart` for traditional Vedic Prashna.
|
|
801
|
-
|
|
802
|
-
## Muhurta Engine
|
|
803
|
-
|
|
804
|
-
```typescript
|
|
805
|
-
import { scoreMuhurta, findAuspiciousDates, vivahRule } from 'panchang-ts';
|
|
806
|
-
|
|
807
|
-
const r = scoreMuhurta(new Date('2026-05-12'), DELHI, vivahRule, { timezone: 330 });
|
|
808
|
-
// → { date, score: 0..100, passes: boolean, reasons: string[] }
|
|
809
|
-
|
|
810
|
-
const dates = findAuspiciousDates(
|
|
811
|
-
vivahRule,
|
|
812
|
-
new Date('2026-05-01'),
|
|
813
|
-
new Date('2026-05-31'),
|
|
814
|
-
DELHI,
|
|
815
|
-
{ timezone: 330 },
|
|
816
|
-
); // MuhurtaDay[] sorted by score desc; full panchang attached
|
|
817
|
-
|
|
818
|
-
// Custom rule (pure data, no engine code needed)
|
|
819
|
-
const myRule: MuhurtaRule = {
|
|
820
|
-
occasion: 'launch_party',
|
|
821
|
-
auspiciousVaras: [3, 4, 5],
|
|
822
|
-
auspiciousNakshatras: [11, 12, 21],
|
|
823
|
-
excludeBhadra: true,
|
|
824
|
-
excludeEkadashi: true,
|
|
825
|
-
excludeAdhikaMasa: true,
|
|
826
|
-
};
|
|
659
|
+
const v = computeVarshaphala(birth, 30, loc); // Tajik + Muntha + 27 Sahams
|
|
660
|
+
computeTithiPravesha(birth, 30, loc); // preserves natal tithi exactly
|
|
661
|
+
computeKpSubLord(45.5).subLord; // 243 sub-divisions
|
|
662
|
+
computePrashnaChart(questionTime, querentLoc); // horary, Placidus-KP default
|
|
827
663
|
```
|
|
828
664
|
|
|
829
|
-
13 stock rules: vivah, griha pravesh, namakarana, vidyarambh, vahan kharidi,
|
|
830
|
-
annaprashan, mundan, upanayanam, karnavedha, aksharabhyasam, seemantham, shop
|
|
831
|
-
opening, travel start.
|
|
832
|
-
|
|
833
|
-
Scoring: starts at 50; +10 per matching auspicious axis (tithi / nakshatra /
|
|
834
|
-
vara / yoga), -15 per inauspicious axis, hard exclusions zero the score.
|
|
835
|
-
Special yogas (Amrit Siddhi, Sarvartha Siddhi, Ravi/Guru Pushya) add +5;
|
|
836
|
-
Jwalamukhi subtracts -10. Clamped 0..100; `passes: true` when score ≥ 50.
|
|
837
|
-
|
|
838
665
|
## Calendar Conversion
|
|
839
666
|
|
|
667
|
+
📖 [Calendar Conversion →](https://dharmagya.app/docs/panchang-ts/calendar-conversion)
|
|
668
|
+
|
|
840
669
|
```typescript
|
|
841
670
|
import {
|
|
842
671
|
convertGregorianToHindu, convertHinduToGregorian,
|
|
843
672
|
getKaliYugaYear, getHinduNewYear,
|
|
844
|
-
|
|
845
|
-
getFestivalsInRange, getUpcomingEclipses, getEclipsesInRange,
|
|
673
|
+
computeEkadashiDatesForYear, computeSankrantisForYear,
|
|
846
674
|
} from 'panchang-ts';
|
|
847
675
|
|
|
848
|
-
|
|
849
|
-
const h = convertGregorianToHindu(new Date('2026-04-15'), DELHI, { timezone: 330 });
|
|
850
|
-
|
|
851
|
-
// Hindu → Gregorian
|
|
852
|
-
const dates = convertHinduToGregorian(
|
|
676
|
+
convertHinduToGregorian(
|
|
853
677
|
{ vikramSamvat: 2083, masaIndex: 0, paksha: 'shukla', pakshaTithi: 9 },
|
|
854
678
|
DELHI, { timezone: 330 },
|
|
855
|
-
);
|
|
856
|
-
|
|
857
|
-
getKaliYugaYear(new Date('2026-04-01')); // 5127
|
|
858
|
-
getHinduNewYear(2026, 'tamil-nadu', DELHI, { timezone: 330 }); // Puthandu
|
|
859
|
-
|
|
860
|
-
getEkadashiDatesForYear(2026, DELHI, { timezone: 330 }); // ~24 Date[]
|
|
861
|
-
getSankrantisForYear(2026, DELHI, { timezone: 330 }); // 12 SankrantiEvent[]
|
|
862
|
-
getFestivalsInRange(start, end, DELHI, { timezone: 330 }); // FestivalDay[]
|
|
863
|
-
getUpcomingEclipses(new Date(), DELHI, 5);
|
|
679
|
+
); // → Date[] (Rama Navami VS 2083)
|
|
680
|
+
getHinduNewYear(2026, 'tamil-nadu', DELHI, { timezone: 330 }); // region-aware
|
|
864
681
|
```
|
|
865
682
|
|
|
866
|
-
`getHinduNewYear` is region-aware: Tamil Nadu / Kerala / Punjab / Bengal / Assam
|
|
867
|
-
use the **solar** (Mesha Sankranti) anchor; elsewhere uses **Chaitra Shukla
|
|
868
|
-
Pratipada** (Ugadi / Gudi Padwa / Cheti Chand). When Pratipada is a kshaya
|
|
869
|
-
tithi (e.g. Ugadi 2026), falls back to the Amanta-Chaitra-masa boundary.
|
|
870
|
-
|
|
871
683
|
## Localization & Configuration
|
|
872
684
|
|
|
873
|
-
|
|
874
|
-
const hi = getDailyPanchang(date, loc, { timezone: 330, language: 'hi' })!;
|
|
875
|
-
hi.tithis[0].name; // "कृष्ण चतुर्दशी"
|
|
876
|
-
hi.vara.name; // "मंगलवार"
|
|
877
|
-
hi.chandramasa.name; // "माघ"
|
|
878
|
-
hi.choghadiya.day[0].name; // "अमृत"
|
|
879
|
-
hi.vara.englishName; // "Tuesday" — englishName always English
|
|
880
|
-
|
|
881
|
-
// All options:
|
|
882
|
-
const r = getDailyPanchang(date, loc, {
|
|
883
|
-
timezone: 330, // number (UTC offset min) or IANA string
|
|
884
|
-
ayanamsa: 'lahiri', // lahiri | raman | krishnamurti | true-chitra | thirukanitham
|
|
885
|
-
language: 'en', // en | hi
|
|
886
|
-
masaSystem: 'purnimanta', // purnimanta | amanta
|
|
887
|
-
region: 'all', // 21 state slugs + 'nepal' + 'all'
|
|
888
|
-
computeEndTimes: true, // false → ~5x speedup, names only
|
|
889
|
-
precision: 'standard', // standard (15 iter) | high (25 iter)
|
|
890
|
-
janmaRashi: undefined, // pass to add r.chandraBalam
|
|
891
|
-
janmaNakshatra: undefined, // pass to add r.tarabala
|
|
892
|
-
});
|
|
893
|
-
```
|
|
894
|
-
|
|
895
|
-
**Timezone.** Number (minutes from UTC, e.g. `330` for IST) or an IANA zone
|
|
896
|
-
name (e.g. `'America/New_York'`). IANA strings need `Intl`, which older Hermes
|
|
897
|
-
versions lack — pass a number on those targets. DST resolves automatically for
|
|
898
|
-
IANA zones.
|
|
899
|
-
|
|
900
|
-
---
|
|
901
|
-
|
|
902
|
-
## Types & Exports
|
|
903
|
-
|
|
904
|
-
<details>
|
|
905
|
-
<summary><strong>Core, Pancha Anga, Festivals</strong></summary>
|
|
906
|
-
|
|
907
|
-
```typescript
|
|
908
|
-
interface GeoLocation { latitude: number; longitude: number; elevation?: number; }
|
|
909
|
-
interface TimePeriod { start: Date; end: Date; }
|
|
910
|
-
|
|
911
|
-
interface TithiInfo {
|
|
912
|
-
index: number; // 0-29
|
|
913
|
-
name: string;
|
|
914
|
-
paksha: string; // "Shukla"/"Krishna" (en), "शुक्ल"/"कृष्ण" (hi)
|
|
915
|
-
number: number; // 1-15 within the paksha
|
|
916
|
-
completionPercentage: number;
|
|
917
|
-
endTime: Date | null;
|
|
918
|
-
}
|
|
919
|
-
// NakshatraInfo, YogaInfo, KaranaInfo follow the same pattern.
|
|
920
|
-
// DailyTithiInfo extends with startTime + isActiveAtSunrise.
|
|
921
|
-
|
|
922
|
-
interface VaraInfo {
|
|
923
|
-
index: number; // 0 = Sunday … 6 = Saturday
|
|
924
|
-
name: string; // localized (e.g. "Raviwara")
|
|
925
|
-
shortName: string;
|
|
926
|
-
englishName: string; // always English
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
interface FestivalInfo {
|
|
930
|
-
name: string;
|
|
931
|
-
type: 'major' | 'minor' | 'ekadashi' | 'smarta_ekadashi' | 'vaishnava_ekadashi'
|
|
932
|
-
| 'pradosha' | 'sankranti' | 'eclipse';
|
|
933
|
-
description?: string;
|
|
934
|
-
deferralDate?: Date; // Smarta Ekadashi → Dwadashi fast date
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
type FestivalRegion =
|
|
938
|
-
| 'all'
|
|
939
|
-
| 'tamil-nadu' | 'kerala' | 'karnataka' | 'andhra-pradesh' | 'telangana'
|
|
940
|
-
| 'west-bengal' | 'odisha' | 'assam' | 'bihar' | 'jharkhand'
|
|
941
|
-
| 'gujarat' | 'maharashtra' | 'goa' | 'rajasthan'
|
|
942
|
-
| 'punjab' | 'haryana' | 'himachal-pradesh' | 'uttarakhand'
|
|
943
|
-
| 'uttar-pradesh' | 'madhya-pradesh'
|
|
944
|
-
| 'nepal';
|
|
945
|
-
|
|
946
|
-
// Legacy slugs accepted (mapped internally): 'tamil' → 'tamil-nadu',
|
|
947
|
-
// 'bengal' → 'west-bengal', 'north-india' → 'all'.
|
|
948
|
-
```
|
|
949
|
-
|
|
950
|
-
</details>
|
|
951
|
-
|
|
952
|
-
<details>
|
|
953
|
-
<summary><strong>Eclipses & Bhadra</strong></summary>
|
|
685
|
+
📖 [Options & Localization →](https://dharmagya.app/docs/panchang-ts/localization)
|
|
954
686
|
|
|
955
687
|
```typescript
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
start: Date; peak: Date; end: Date;
|
|
960
|
-
visibleFromLocation: boolean;
|
|
961
|
-
magnitude: number; // [0, 1] at peak
|
|
962
|
-
sutakStart: Date; // 12 h pre-solar / 9 h pre-lunar
|
|
963
|
-
sutakEnd: Date;
|
|
964
|
-
description: string;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
interface BhadraInfo {
|
|
968
|
-
start: Date; end: Date;
|
|
969
|
-
location: 'earth' | 'heaven' | 'paatal'; // 'earth' = malefic for all work
|
|
970
|
-
isActive: boolean;
|
|
971
|
-
}
|
|
972
|
-
```
|
|
973
|
-
|
|
974
|
-
</details>
|
|
975
|
-
|
|
976
|
-
<details>
|
|
977
|
-
<summary><strong>Jyotish</strong></summary>
|
|
978
|
-
|
|
979
|
-
```typescript
|
|
980
|
-
type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter'
|
|
981
|
-
| 'Venus' | 'Saturn' | 'Rahu' | 'Ketu';
|
|
982
|
-
|
|
983
|
-
interface GrahaPosition {
|
|
984
|
-
planet: GrahaName;
|
|
985
|
-
siderealLongitude: number;
|
|
986
|
-
rashi: RashiInfo;
|
|
987
|
-
degreeInRashi: number;
|
|
988
|
-
nakshatra: NakshatraInfo;
|
|
989
|
-
isRetrograde: boolean; // always false for Sun/Moon; always true for Rahu/Ketu
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars'
|
|
993
|
-
| 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
|
|
994
|
-
|
|
995
|
-
interface MahaDasha { lord: DashaLord; startDate: Date; endDate: Date;
|
|
996
|
-
years: number; antarDashas: AntarDasha[]; }
|
|
997
|
-
interface VimshottariDashaResult {
|
|
998
|
-
currentMahaDashaLord: DashaLord;
|
|
999
|
-
currentIndex: number;
|
|
1000
|
-
mahaDashas: MahaDasha[];
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
interface ChandraBalamInfo {
|
|
1004
|
-
house: number; // 1 = janma rashi; 12 = rashi before janma
|
|
1005
|
-
quality: 'strong' | 'weak'; // Shubha houses = 1,3,6,7,10,11
|
|
1006
|
-
englishName: string; // "Shubha" | "Ashubha"
|
|
1007
|
-
name: string;
|
|
1008
|
-
}
|
|
688
|
+
const hi = getDailyPanchang(date, loc, { timezone: 330, language: 'hi' })!;
|
|
689
|
+
hi.angas.tithis[0].name; // "कृष्ण चतुर्दशी"
|
|
690
|
+
hi.angas.vara.englishName; // "Tuesday" — englishName always English
|
|
1009
691
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
// | Sadhaka | Vadha | Mitra | Ati-Mitra
|
|
1014
|
-
name: string;
|
|
1015
|
-
quality: 'auspicious' | 'inauspicious';
|
|
1016
|
-
}
|
|
692
|
+
// All options: timezone (number | IANA string), ayanamsa (5), language (en|hi),
|
|
693
|
+
// masaSystem (purnimanta|amanta), region, computeEndTimes, sections,
|
|
694
|
+
// janmaRashi, janmaNakshatra.
|
|
1017
695
|
```
|
|
1018
696
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
<summary><strong>Full export list</strong></summary>
|
|
697
|
+
Machine-readable keys never change with language: match on `festival.key`,
|
|
698
|
+
`bhadra.location`, `eclipse.kind`, `factors[].code` — render `name` /
|
|
699
|
+
`locationName` / `description` / `reasons`.
|
|
1023
700
|
|
|
1024
|
-
|
|
1025
|
-
// Primary entry points
|
|
1026
|
-
getDailyPanchang, getInstantPanchang
|
|
1027
|
-
|
|
1028
|
-
// Astronomy
|
|
1029
|
-
getSunrise, getSunset, getMoonrise, getMoonset
|
|
1030
|
-
getSiderealSunLongitude, getSiderealMoonLongitude, getAyanamsa
|
|
1031
|
-
|
|
1032
|
-
// Inauspicious / Muhurta
|
|
1033
|
-
computeRahuKalam, computeGulikaKalam, computeYamaganda
|
|
1034
|
-
computeVarjyam, computeGandaMula, computeAnandadiYoga
|
|
1035
|
-
computePanchakaRahita, computeDoGhati, computeGowriPanchangam
|
|
1036
|
-
computeAbhijitMuhurta, computeBrahmaMuhurta, computeVijayaMuhurta
|
|
1037
|
-
computeGodhuliMuhurta, computeNishitaMuhurta, computeAmritKala
|
|
1038
|
-
computeMadhyahna, computePratahSandhya, computeSayahnaSandhya
|
|
1039
|
-
|
|
1040
|
-
// Eclipses
|
|
1041
|
-
getUpcomingSolarEclipse, getUpcomingLunarEclipse, getEclipseDuringDay
|
|
1042
|
-
isEclipseVisibleAnyPhase
|
|
1043
|
-
|
|
1044
|
-
// Moon phases (new / quarters / full as precise instants)
|
|
1045
|
-
getMoonPhasesInRange
|
|
1046
|
-
|
|
1047
|
-
// Jyotish — planets, dashas, transits
|
|
1048
|
-
computePlanetaryPositions, GRAHA_ABBR
|
|
1049
|
-
computeVimshottariDasha, computeVimshottariDashaFromBirth, computeVimshottariPratyantar
|
|
1050
|
-
computeAshtottariDasha, computeYoginiDasha, computeCharaDasha, computeNarayanDasha
|
|
1051
|
-
computeChandraBalam, computeTarabala, computeSadeSati
|
|
1052
|
-
|
|
1053
|
-
// Jyotish — chart
|
|
1054
|
-
computeLagna, computeBhava, computeRashiChart, computeNavamsa, computeDivisionalChart
|
|
1055
|
-
computeHoraLagna, computeGhatiLagna, computeBhavaLagna, computeSripatiLagna
|
|
1056
|
-
computeDignity
|
|
1057
|
-
|
|
1058
|
-
// Jyotish — strength, yogas, sensitive
|
|
1059
|
-
computeAspects, computeShadbala, computeBhavaBala, computeAshtakavarga
|
|
1060
|
-
computeYogas, computeJaiminiKarakas
|
|
1061
|
-
computeVarshaphala, computeTithiPravesha, computeArudhas, computeUpagrahas, computeArgala
|
|
1062
|
-
|
|
1063
|
-
// Jyotish — compatibility, doshas
|
|
1064
|
-
computeAshtakoot, computePathuPorutham
|
|
1065
|
-
computeMangalDosha, computeKaalSarp, computePitruDosha
|
|
1066
|
-
|
|
1067
|
-
// KP / Prashna
|
|
1068
|
-
computeKpSubLord, computeKpCuspalSubLords, computeKpSignificators
|
|
1069
|
-
computePrashnaChart
|
|
1070
|
-
|
|
1071
|
-
// Muhurta engine
|
|
1072
|
-
scoreMuhurta, findAuspiciousDates, STOCK_MUHURTA_RULES
|
|
1073
|
-
vivahRule, grihaPraveshRule, namakaranaRule, vidyarambhRule, vahanKharidiRule
|
|
1074
|
-
annaprashanRule, mundanRule, upanayanamRule, karnavedhaRule
|
|
1075
|
-
aksharabhyasamRule, seemanthamRule, shopOpeningRule, travelStartRule
|
|
1076
|
-
|
|
1077
|
-
// Calendar conversion + yearly listings
|
|
1078
|
-
convertGregorianToHindu, convertHinduToGregorian
|
|
1079
|
-
getKaliYugaYear, getHinduNewYear, computeSamvat
|
|
1080
|
-
getEkadashiDatesForYear, getSankrantisForYear, getFestivalsInRange
|
|
1081
|
-
getUpcomingEclipses, getEclipsesInRange
|
|
1082
|
-
|
|
1083
|
-
// Static data tables (engine-using runtime builders; bundled JSON at
|
|
1084
|
-
// panchang-ts/festivals, panchang-ts/eclipses, panchang-ts/moon-phases)
|
|
1085
|
-
buildFestivalsTable, buildEclipsesTable, buildMoonPhasesTable
|
|
1086
|
-
|
|
1087
|
-
// Errors
|
|
1088
|
-
PanchangError
|
|
1089
|
-
```
|
|
701
|
+
## Types & Exports
|
|
1090
702
|
|
|
1091
|
-
|
|
703
|
+
📖 [Types & Exports →](https://dharmagya.app/docs/panchang-ts/types) — the key
|
|
704
|
+
interfaces (`TithiInfo`, `FestivalInfo`, `EclipseInfo`, `GrahaPosition`, …) and
|
|
705
|
+
the complete export list of the main entry and the four engine-free subpaths
|
|
706
|
+
(`panchang-ts/festivals`, `/eclipses`, `/moon-phases`, `/muhurta`).
|
|
1092
707
|
|
|
1093
708
|
---
|
|
1094
709
|
|
|
@@ -1103,14 +718,15 @@ Two-pass rendering pattern for smooth UI:
|
|
|
1103
718
|
import { getDailyPanchang } from 'panchang-ts';
|
|
1104
719
|
import { InteractionManager } from 'react-native';
|
|
1105
720
|
|
|
1106
|
-
// Pass 1 —
|
|
721
|
+
// Pass 1 — cheapest useful result: elements, slots, muhurtas (~0.25 ms).
|
|
1107
722
|
const fast = getDailyPanchang(date, location, {
|
|
1108
723
|
timezone: 330,
|
|
724
|
+
sections: [],
|
|
1109
725
|
computeEndTimes: false,
|
|
1110
726
|
});
|
|
1111
727
|
setState(fast);
|
|
1112
728
|
|
|
1113
|
-
// Pass 2 — background,
|
|
729
|
+
// Pass 2 — background, everything (~0.41 ms).
|
|
1114
730
|
InteractionManager.runAfterInteractions(() => {
|
|
1115
731
|
setState(getDailyPanchang(date, location, { timezone: 330 }));
|
|
1116
732
|
});
|
|
@@ -1120,7 +736,9 @@ InteractionManager.runAfterInteractions(() => {
|
|
|
1120
736
|
|
|
1121
737
|
## Accuracy
|
|
1122
738
|
|
|
1123
|
-
|
|
739
|
+
📖 [Full accuracy notes →](https://dharmagya.app/docs/panchang-ts/accuracy)
|
|
740
|
+
|
|
741
|
+
8,368 tests across 121 files, including fixtures cross-verified against reference
|
|
1124
742
|
panchang calculations spanning 2025–2026 across 10 Indian cities plus New York,
|
|
1125
743
|
London, Sydney, Dubai, Singapore (diaspora fixtures cover DST on
|
|
1126
744
|
`America/New_York`).
|
|
@@ -1130,54 +748,68 @@ London, Sydney, Dubai, Singapore (diaspora fixtures cover DST on
|
|
|
1130
748
|
| Sunrise / Sunset | ≤29 s observed vs reference minute-midpoint (±45 s tolerance) |
|
|
1131
749
|
| Moonrise / Moonset | Meeus apparent-upper-limb (refraction + parallax); ~3–5 min vs simpler-horizon authorities is expected |
|
|
1132
750
|
| Tithi / Nakshatra / Yoga / Karana names | Exact match vs reference |
|
|
1133
|
-
| Tithi / Nakshatra / Yoga / Karana end-times |
|
|
1134
|
-
| Ayanamsa |
|
|
751
|
+
| Tithi / Nakshatra / Yoga / Karana end-times | ≤60 s vs Drik across all 20 audited comparisons |
|
|
752
|
+
| Ayanamsa (Lahiri) | Reproduces DrikPanchang's published value to ~0.01″ across 1950–2050 |
|
|
1135
753
|
| Planetary positions (Sun–Saturn) | ±0.02° sidereal |
|
|
1136
|
-
| Planetary positions (Rahu/Ketu
|
|
1137
|
-
| Planetary positions (Rahu/Ketu, true node) | ≤0.6° typical (Meeus periodic correction) |
|
|
754
|
+
| Planetary positions (Rahu/Ketu) | ≤0.5° mean node, ≤0.6° true node (typical) |
|
|
1138
755
|
| Lagna sidereal longitude | Cross-checked against Jagannath Hora reference charts |
|
|
1139
756
|
| D1 / D9 house placement | Exact match vs reference for 9-graha placement |
|
|
1140
757
|
| Ashtakoot total | ±1 point per pair across 30+ matched pairs |
|
|
1141
758
|
| Sade Sati arc start/end | ±1–2 days vs authoritative ephemerides |
|
|
1142
759
|
|
|
1143
|
-
**
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
760
|
+
**Festival dating** uses tithi-at-sunrise; a few festivals have authorities on
|
|
761
|
+
other rules (tithi-at-midnight for Janmashtami / Shivaratri / Diwali,
|
|
762
|
+
madhyahna-vyapini for Ganesh Chaturthi edge years) where output can drift
|
|
763
|
+
±1 day — the exact list is
|
|
764
|
+
[documented](https://dharmagya.app/docs/panchang-ts/accuracy#festival-tradeoff).
|
|
1148
765
|
|
|
1149
|
-
|
|
766
|
+
**Detection conventions:** Aadal / Vidaal follow the classical Moon-from-Sun
|
|
767
|
+
nakshatra-distance rule, not the Tamil-Vakya weekday rule. Varjyam emits the
|
|
768
|
+
sunrise-anchored nakshatra's window only. Do Ghati does not rotate by weekday.
|
|
1150
769
|
|
|
1151
|
-
|
|
1152
|
-
Some authorities use other classical rules for certain festivals; where those
|
|
1153
|
-
rules pick a different day, output can drift ±1 day:
|
|
770
|
+
---
|
|
1154
771
|
|
|
1155
|
-
|
|
1156
|
-
|---|---|
|
|
1157
|
-
| Tithi-at-midnight | Krishna Janmashtami, Maha Shivaratri, Diwali / Lakshmi Puja |
|
|
1158
|
-
| Madhyahna-vyapini | Ganesh Chaturthi (edge years), Akshaya Tritiya 2026 |
|
|
1159
|
-
| Kshaya-tithi handling | Ugadi 2026-03-19 (Pratipada is Kshaya) |
|
|
772
|
+
## Performance
|
|
1160
773
|
|
|
1161
|
-
|
|
1162
|
-
Ganesh Chaturthi (normal years), Navaratri, Dussehra, Karva Chauth, Hanuman
|
|
1163
|
-
Jayanti — matches the canonical date across 2025 and 2026 fixtures.
|
|
774
|
+
📖 [Full performance notes →](https://dharmagya.app/docs/panchang-ts/performance)
|
|
1164
775
|
|
|
1165
|
-
|
|
776
|
+
Measured at Pune, Apple M-series, Node — median of 11 processes. **Distinct
|
|
777
|
+
days** is the calendar-scan cost; **same day repeated** is what a UI
|
|
778
|
+
re-rendering one date sees. Last column is published 4.3.1, benchmarked beside
|
|
779
|
+
this release.
|
|
1166
780
|
|
|
1167
|
-
|
|
781
|
+
| `getDailyPanchang` call | Distinct days | Same day repeated | 4.3.1 (distinct) |
|
|
782
|
+
|---|---|---|---|
|
|
783
|
+
| Default (all sections + end-times) | **~0.41 ms** | **~0.17 ms** | ~6.06 ms |
|
|
784
|
+
| `sections: []` + `computeEndTimes: false` | ~0.25 ms | ~0.14 ms | n/a |
|
|
785
|
+
| `getInstantPanchang` | ~0.21 ms | ~0.10 ms | ~0.43 ms |
|
|
1168
786
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
787
|
+
**A default day is ~15× cheaper than 4.3.1**, a repeated day ~37×. The levers:
|
|
788
|
+
|
|
789
|
+
- **`sections`** — skip the optional ephemeris-backed blocks (`'festivals'`,
|
|
790
|
+
`'eclipse'`, `'moonTimes'`, `'lunarWindows'`). Narrowing is exactly
|
|
791
|
+
output-neutral: every field a narrowed call computes is identical to the full
|
|
792
|
+
call's; omitted sections sit at their documented `null` / `[]`.
|
|
793
|
+
- **`computeEndTimes: false`** — drops the `endTime` transition searches;
|
|
794
|
+
~5–10% — use it to drop fields you don't want, not to go faster.
|
|
1173
795
|
|
|
1174
|
-
|
|
1175
|
-
`
|
|
796
|
+
Range helpers narrow internally: `computeEkadashiDatesForYear` **~18 ms/year**
|
|
797
|
+
(4.3.1: ~2,360), `computeFestivalsInRange` **~131 ms/year** (~2,180),
|
|
798
|
+
`computeSankrantisForYear` **~3.3 ms/year** (~154). Solar rise/set events are
|
|
799
|
+
cached process-wide (bounded, 20k entries), which also makes them
|
|
800
|
+
single-valued.
|
|
801
|
+
|
|
802
|
+
One deliberate regression: raw chart primitives (`computeRashiChart`,
|
|
803
|
+
`computeNavamsa`) cost ~0.32 ms vs ~0.10 in 4.3.1 — the price of an
|
|
804
|
+
order-of-magnitude accuracy gain against JPL DE441. `computeShadbala` /
|
|
805
|
+
`computeBhavaBala` went the other way, ~2× faster.
|
|
1176
806
|
|
|
1177
807
|
---
|
|
1178
808
|
|
|
1179
809
|
## Error Handling
|
|
1180
810
|
|
|
811
|
+
📖 [Errors & Compatibility →](https://dharmagya.app/docs/panchang-ts/errors)
|
|
812
|
+
|
|
1181
813
|
```typescript
|
|
1182
814
|
import { PanchangError } from 'panchang-ts';
|
|
1183
815
|
|
|
@@ -1192,7 +824,7 @@ try {
|
|
|
1192
824
|
```
|
|
1193
825
|
|
|
1194
826
|
Error codes: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
|
|
1195
|
-
`INVALID_ELEVATION`, `INVALID_TIMEZONE`, `INVALID_AYANAMSA`,
|
|
827
|
+
`INVALID_ELEVATION`, `INVALID_TIMEZONE`, `INVALID_AYANAMSA`, `INVALID_INPUT`,
|
|
1196
828
|
`TIMEZONE_RESOLUTION_FAILED`, `NO_SUNRISE`, `NO_SUNSET`, `SEARCH_DIVERGED`,
|
|
1197
829
|
`CIRCUMPOLAR` (Placidus-KP houses above ±66.5°).
|
|
1198
830
|
|
|
@@ -1209,18 +841,25 @@ for the Moon).
|
|
|
1209
841
|
|
|
1210
842
|
| Environment | Support |
|
|
1211
843
|
|---|---|
|
|
1212
|
-
| Node.js
|
|
844
|
+
| Node.js ≥ 22 (per the package `engines` field) | Supported |
|
|
1213
845
|
| React Native (Hermes) | Supported (pass `timezone` as number) |
|
|
1214
846
|
| Expo (managed + bare) | Supported |
|
|
1215
847
|
| Browser (modern, ESM) | Supported |
|
|
1216
848
|
| Browser (legacy / IE) | Not supported |
|
|
1217
849
|
|
|
850
|
+
The build targets ES2020 (ESM + CJS, full `.d.ts`), has **zero runtime
|
|
851
|
+
dependencies**, and is `sideEffects: false`.
|
|
852
|
+
|
|
1218
853
|
---
|
|
1219
854
|
|
|
1220
855
|
## Acknowledgements
|
|
1221
856
|
|
|
1222
|
-
|
|
1223
|
-
|
|
857
|
+
As of v5 the package has **no runtime dependencies** — the ephemeris, ΔT model
|
|
858
|
+
and event searches are the library's own. Two projects still deserve credit:
|
|
859
|
+
[astronomy-engine](https://github.com/cosinekitty/astronomy) by Don Cross (MIT),
|
|
860
|
+
the runtime engine through 4.x and now the dev-time baseline the own ephemeris
|
|
861
|
+
is tested against, and the algorithms of Jean Meeus's *Astronomical Algorithms*,
|
|
862
|
+
which underpin the rise/set and node models.
|
|
1224
863
|
|
|
1225
864
|
## License
|
|
1226
865
|
|