panchang-ts 0.6.1 → 1.0.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 CHANGED
@@ -1,31 +1,32 @@
1
1
  # panchang-ts
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/panchang-ts)](https://www.npmjs.com/package/panchang-ts)
4
+
3
5
  Pure TypeScript Hindu Panchang (almanac) calculations. Zero native dependencies.
4
6
  Works offline in React Native (Hermes), Node.js, and browsers.
5
7
 
6
- ## Features
8
+ **Fast** (~0.1 ms names-only, ~0.5 ms full) | **Typed** (full TypeScript types) | **Offline** (pure JS math, no network)
9
+
10
+ ---
7
11
 
8
- - **Pancha Anga (5 limbs):** Tithi, Nakshatra, Yoga, Karana, Vara — with mid-day transition times
9
- - **Lunar calendar:** Chandra Masa (lunar month + Adhika/leap detection), Vikram Samvat, Shaka Samvat
10
- - **Zodiac & asterism:** Chandra Rashi (Moon sign), Surya Nakshatra (Sun's asterism)
11
- - **Muhurta:** Brahma Muhurta, Abhijit Muhurta
12
- - **Inauspicious periods:** Rahu Kalam, Gulika Kalam, Yamaganda
13
- - **Choghadiya:** 16 time slots (8 day + 8 night), each named and rated auspicious/neutral/inauspicious
14
- - **Gowri Panchangam:** 16 Gowri Nalla Neram slots (8 day + 8 night) with 8-name cycle, vara-based starting index
15
- - **Hora:** 24 planetary hours per day (12 day + 12 night) in Chaldean order
16
- - **Astronomical events:** Sunrise, Sunset, Moonrise, Moonset
17
- - **Panchaka detection:** Flag when Moon is in the last 5 nakshatras (Dhanishta 3rd pada → Revati)
18
- - **Special Yogas:** Amrit Siddhi, Sarvartha Siddhi, Ravi Pushya, Guru Pushya — detected from Vara × Tithi/Nakshatra tables
19
- - **Dur Muhurta:** Two ~48-minute inauspicious windows per day, position varies by Vara
20
- - **Festival detection:** 24 major pan-Indian festivals, recurring Ekadashi & Pradosha Vrata, Sankranti; skips Adhika (leap) months automatically
21
- - **Daily mode:** Full sunrise-to-sunrise day with all element transitions
22
- - **Instant mode:** Elements active at an exact moment (birth charts, muhurta selection)
23
- - **3 ayanamsa systems:** Lahiri (default), B.V. Raman, KP (Krishnamurti)
24
- - **3 languages:** English, Sanskrit (Devanagari), Hindi
25
- - **Jyotish (Vedic astrology):** All 9 graha positions (geocentric, sidereal), Vimshottari Dasha with Antardasha breakdown
26
- - **React Native compatible:** Pure JS math, no native modules, tested on Hermes
27
- - **Fast:** ~0.1 ms names-only on Node.js; <100 ms on budget Android (Hermes)
28
- - **Typed:** Full TypeScript types for every result and option
12
+ ## Table of Contents
13
+
14
+ - [Install](#install)
15
+ - [Quick Start](#quick-start)
16
+ - [Features](#features)
17
+ - [API Reference](#api-reference)
18
+ - [`getDailyPanchang`](#getdailypanchangdate-location-options)
19
+ - [`getInstantPanchang`](#getinstantpanchangdate-location-options)
20
+ - [Options](#options)
21
+ - [Low-level Utilities](#low-level-utilities)
22
+ - [Types](#types)
23
+ - [React Native / Hermes](#react-native--hermes)
24
+ - [Accuracy](#accuracy)
25
+ - [Performance](#performance)
26
+ - [Error Handling](#error-handling)
27
+ - [Compatibility](#compatibility)
28
+
29
+ ---
29
30
 
30
31
  ## Install
31
32
 
@@ -53,10 +54,10 @@ console.log(result.tithis[0].name); // "Krishna Chaturdashi"
53
54
  console.log(result.nakshatras[0].name); // "Mrigashira"
54
55
  console.log(result.vara.name); // "Mangalavara"
55
56
 
56
- // Lunar calendar
57
- console.log(result.chandramasa.name); // "Pausha"
57
+ // Lunar calendar (Purnimanta by default)
58
+ console.log(result.chandramasa.name); // "Magha"
59
+ console.log(result.chandramasa.amantaName); // "Pausha" (South Indian)
58
60
  console.log(result.samvat.vikramSamvat); // 2081
59
- console.log(result.samvat.shakaSamvat); // 1946
60
61
 
61
62
  // Zodiac
62
63
  console.log(result.chandraRashi.name); // "Mithuna" (Moon in Gemini)
@@ -66,7 +67,7 @@ console.log(result.suryaNakshatra.name); // "Uttara Ashadha"
66
67
  console.log(result.sunrise); // Date (read via getUTC*)
67
68
  console.log(result.moonrise); // Date | null
68
69
 
69
- // Muhurta
70
+ // Muhurta & inauspicious periods
70
71
  console.log(result.brahmaMuhurta); // { start: Date, end: Date }
71
72
  console.log(result.rahuKalam); // { start: Date, end: Date }
72
73
 
@@ -75,36 +76,26 @@ result.choghadiya.day.forEach(slot => {
75
76
  console.log(slot.name, slot.qualityName); // "Amrit", "Auspicious"
76
77
  });
77
78
 
78
- // Panchaka
79
- console.log(result.panchaka); // true | false
79
+ // Gowri Panchangam — 8 daytime slots
80
+ result.gowriPanchangam.day.forEach(slot => {
81
+ console.log(slot.name, slot.qualityName); // "Amrit", "Auspicious"
82
+ });
80
83
 
81
84
  // Special Yogas active today
82
85
  result.specialYogas.forEach(yoga => {
83
86
  console.log(yoga.name, yoga.type); // "Guru Pushya Yoga", "guru_pushya"
84
87
  });
85
88
 
86
- // Dur Muhurta — two inauspicious windows
87
- const [dm1, dm2] = result.durMuhurta;
88
- console.log(fmt(dm1.start), '-', fmt(dm1.end)); // e.g. "11:36 - 12:24"
89
- console.log(fmt(dm2.start), '-', fmt(dm2.end));
90
-
91
89
  // Festivals today
92
90
  result.festivals.forEach(f => {
93
- console.log(f.name, f.type); // "Diwali", "major"
94
- });
95
-
96
- // Gowri Panchangam — 8 daytime slots
97
- result.gowriPanchangam.day.forEach(slot => {
98
- console.log(slot.name, slot.qualityName); // "Amrit", "Auspicious"
91
+ console.log(f.name, f.type); // "Makar Sankranti", "major"
99
92
  });
100
-
101
-
102
93
  ```
103
94
 
104
- ## Reading Output Times
95
+ ### Reading Output Times
105
96
 
106
97
  All `Date` objects in the result are **offset-adjusted** to the requested timezone.
107
- **Always read time components via `getUTC*` methods:**
98
+ Always read time components via `getUTC*` methods:
108
99
 
109
100
  ```typescript
110
101
  const sunrise = result.sunrise;
@@ -125,6 +116,64 @@ Do **not** use `.getHours()` — it uses your system timezone, which may differ.
125
116
  `moonrise` and `moonset` can be `null` — the Moon occasionally does not rise or set
126
117
  on a given calendar day, which is normal.
127
118
 
119
+ ### Language & Masa System
120
+
121
+ ```typescript
122
+ // Sanskrit names (Devanagari)
123
+ const sa = getDailyPanchang(date, location, {
124
+ timezone: 330,
125
+ language: 'sa',
126
+ });
127
+ console.log(sa.tithis[0].name); // "कृष्ण चतुर्दशी"
128
+ console.log(sa.vara.name); // "मङ्गलवारः"
129
+
130
+ // Hindi names
131
+ const hi = getDailyPanchang(date, location, {
132
+ timezone: 330,
133
+ language: 'hi',
134
+ });
135
+ console.log(hi.tithis[0].name); // "कृष्ण चतुर्दशी"
136
+
137
+ // Amanta (South Indian) masa system
138
+ const amanta = getDailyPanchang(date, location, {
139
+ timezone: 330,
140
+ masaSystem: 'amanta',
141
+ });
142
+ console.log(amanta.chandramasa.name); // Amanta month name
143
+ console.log(amanta.chandramasa.system); // "amanta"
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Features
149
+
150
+ ### Pancha Anga (5 Limbs)
151
+ Tithi, Nakshatra, Yoga, Karana, Vara — with transition times throughout the day.
152
+
153
+ ### Lunar Calendar
154
+ Chandra Masa with Adhika (leap month) detection, both **Purnimanta** (North Indian, default) and **Amanta** (South Indian) systems, Vikram Samvat, Shaka Samvat.
155
+
156
+ ### Muhurta & Auspicious Timing
157
+ Brahma Muhurta, Abhijit Muhurta, Choghadiya (16 slots), Gowri Panchangam / Nalla Neram (16 slots), Hora (24 planetary hours), Dur Muhurta (2 inauspicious windows).
158
+
159
+ ### Inauspicious Periods
160
+ Rahu Kalam, Gulika Kalam, Yamaganda, Panchaka detection.
161
+
162
+ ### Special Yogas & Festivals
163
+ Amrit Siddhi, Sarvartha Siddhi, Ravi Pushya, Guru Pushya yoga detection. 24 major pan-Indian festivals, recurring Ekadashi & Pradosha Vrata, Sankranti — Adhika months auto-skipped.
164
+
165
+ ### Jyotish (Vedic Astrology)
166
+ All 9 graha positions (geocentric, sidereal) with rashi, nakshatra, pada, and retrograde status. Vimshottari Dasha with Antardasha breakdown — from a birth moment alone or from an explicit Moon longitude. Chandra Balam (transit-Moon favorability relative to janma rashi).
167
+
168
+ ### Astronomy
169
+ Sunrise, Sunset, Moonrise, Moonset, Chandra Rashi (Moon sign), Surya Nakshatra.
170
+
171
+ ### Localization
172
+ 3 languages: **English**, **Sanskrit** (Devanagari), **Hindi**. All returned display strings respect the `language` option.
173
+
174
+ ### Configuration
175
+ 3 ayanamsa systems (Lahiri, B.V. Raman, KP), 2 masa systems (Purnimanta, Amanta), adjustable precision, optional fast mode (`computeEndTimes: false` for ~5x speedup).
176
+
128
177
  ---
129
178
 
130
179
  ## API Reference
@@ -155,34 +204,33 @@ const result = getDailyPanchang(
155
204
  | `nextSunrise` | `Date` | Following day's sunrise (offset-adjusted) |
156
205
  | `dayDurationMinutes` | `number` | Length of daytime in minutes |
157
206
  | `nightDurationMinutes` | `number` | Length of night in minutes |
158
- | `tithis` | `DailyTithiInfo[]` | Tithis active during the day (usually 1–2) |
207
+ | `tithis` | `DailyTithiInfo[]` | Tithis active during the day (usually 1-2) |
159
208
  | `nakshatras` | `DailyNakshatraInfo[]` | Nakshatras active during the day |
160
209
  | `yogas` | `DailyYogaInfo[]` | Yogas active during the day |
161
- | `karanas` | `DailyKaranaInfo[]` | Karanas active during the day (usually 2–4) |
210
+ | `karanas` | `DailyKaranaInfo[]` | Karanas active during the day (usually 2-4) |
162
211
  | `vara` | `VaraInfo` | Weekday (Vara) |
163
212
  | `rahuKalam` | `TimePeriod` | Rahu Kalam start/end |
164
213
  | `gulikaKalam` | `TimePeriod` | Gulika Kalam start/end |
165
214
  | `yamaganda` | `TimePeriod` | Yamaganda start/end |
166
215
  | `abhijitMuhurta` | `TimePeriod` | Abhijit Muhurta start/end |
167
- | `brahmaMuhurta` | `TimePeriod` | Brahma Muhurta — two muhurtas (dayDuration/30 each) before sunrise; ends one muhurta before sunrise (≈ 48–24 min window for typical 12-h days) |
216
+ | `brahmaMuhurta` | `TimePeriod` | Brahma Muhurta — two muhurtas before sunrise |
168
217
  | `masa` | `MasaInfo` | Solar month (Saura Masa) |
169
218
  | `chandramasa` | `ChandraMasaInfo` | Lunar month + Adhika (leap) flag |
170
219
  | `samvat` | `SamvatInfo` | Vikram Samvat and Shaka Samvat year numbers |
171
220
  | `chandraRashi` | `RashiInfo` | Moon's zodiac sign (changes every ~2.5 days) |
172
- | `suryaNakshatra` | `RashiInfo` | Sun's nakshatra (changes every ~13–14 days) |
173
- | `choghadiya` | `ChoghadiyaInfo` | 8 day slots + 8 night slots, each named and rated |
174
- | `gowriPanchangam` | `GowriInfo` | 8 day + 8 night Gowri Nalla Neram slots, each named and rated |
175
-
176
- | `hora` | `HoraInfo` | 12 day horas + 12 night horas, each with ruling planet |
177
- | `moonrise` | `Date \| null` | Moonrise (offset-adjusted); `null` if none that day |
178
- | `moonset` | `Date \| null` | Moonset (offset-adjusted); `null` if none that day |
221
+ | `suryaNakshatra` | `RashiInfo` | Sun's nakshatra (changes every ~13-14 days) |
222
+ | `choghadiya` | `ChoghadiyaInfo` | 8 day + 8 night slots, each named and rated |
223
+ | `gowriPanchangam` | `GowriInfo` | 8 day + 8 night Gowri Nalla Neram slots |
224
+ | `hora` | `HoraInfo` | 12 day + 12 night horas, each with ruling planet |
225
+ | `moonrise` | `Date \| null` | Moonrise; `null` if none that day |
226
+ | `moonset` | `Date \| null` | Moonset; `null` if none that day |
179
227
  | `panchaka` | `boolean` | `true` when Moon is in last 5 nakshatras |
180
- | `specialYogas` | `SpecialYogaInfo[]` | Auspicious yogas active today (may be empty) |
228
+ | `specialYogas` | `SpecialYogaInfo[]` | Auspicious yogas active today |
181
229
  | `durMuhurta` | `[TimePeriod, TimePeriod]` | Two inauspicious ~48-min windows |
182
- | `festivals` | `FestivalInfo[]` | Festivals / observances today (may be empty) |
230
+ | `festivals` | `FestivalInfo[]` | Festivals / observances today |
183
231
  | `ayanamsa` | `number` | Ayanamsa in degrees at sunrise |
184
- | `siderealSunAtSunrise` | `number` | Sun sidereal longitude at sunrise (°) |
185
- | `siderealMoonAtSunrise` | `number` | Moon sidereal longitude at sunrise (°) |
232
+ | `siderealSunAtSunrise` | `number` | Sun sidereal longitude at sunrise (degrees) |
233
+ | `siderealMoonAtSunrise` | `number` | Moon sidereal longitude at sunrise (degrees) |
186
234
 
187
235
  ---
188
236
 
@@ -201,7 +249,7 @@ const result = getInstantPanchang(
201
249
 
202
250
  console.log(result.tithi.name); // "कृष्ण चतुर्दशी"
203
251
  console.log(result.nakshatra.name); // "मृगशिरा"
204
- console.log(result.chandramasa.name); // "पौष"
252
+ console.log(result.chandramasa.name); // "माघ"
205
253
  console.log(result.chandraRashi.name); // "मिथुन"
206
254
  console.log(result.samvat.vikramSamvat); // 2081
207
255
  console.log(result.panchaka); // false
@@ -223,11 +271,11 @@ console.log(result.panchaka); // false
223
271
  | `chandraRashi` | `RashiInfo` | Moon's zodiac sign |
224
272
  | `suryaNakshatra` | `RashiInfo` | Sun's nakshatra |
225
273
  | `panchaka` | `boolean` | `true` when Moon is in last 5 nakshatras |
226
- | `specialYogas` | `SpecialYogaInfo[]` | Auspicious yogas at this moment (may be empty) |
227
- | `festivals` | `FestivalInfo[]` | Festivals / observances at this moment (may be empty) |
274
+ | `specialYogas` | `SpecialYogaInfo[]` | Auspicious yogas at this moment |
275
+ | `festivals` | `FestivalInfo[]` | Festivals / observances at this moment |
228
276
  | `ayanamsa` | `number` | Ayanamsa in degrees |
229
- | `siderealSun` | `number` | Sun sidereal longitude (°) |
230
- | `siderealMoon` | `number` | Moon sidereal longitude (°) |
277
+ | `siderealSun` | `number` | Sun sidereal longitude (degrees) |
278
+ | `siderealMoon` | `number` | Moon sidereal longitude (degrees) |
231
279
 
232
280
  ---
233
281
 
@@ -239,9 +287,10 @@ console.log(result.panchaka); // false
239
287
  |--------|------|---------|-------------|
240
288
  | `timezone` | `number \| string` | **required** | UTC offset in minutes (330 for IST). Use a number on Hermes — IANA strings require `Intl`. |
241
289
  | `ayanamsa` | `'lahiri' \| 'raman' \| 'krishnamurti'` | `'lahiri'` | Ayanamsa system |
242
- | `language` | `'en' \| 'sa' \| 'hi'` | `'en'` | Language for all element names (tithi, paksha, masa, etc.). `'sa'` = classical Sanskrit Devanagari, `'hi'` = modern Hindi Devanagari. |
243
- | `computeEndTimes` | `boolean` | `true` | Set `false` for ~5× faster, names-only output |
290
+ | `language` | `'en' \| 'sa' \| 'hi'` | `'en'` | Language for all element names. `'sa'` = classical Sanskrit Devanagari, `'hi'` = modern Hindi Devanagari. |
291
+ | `computeEndTimes` | `boolean` | `true` | Set `false` for ~5x faster, names-only output |
244
292
  | `precision` | `'standard' \| 'high'` | `'standard'` | Binary-search iterations (15 vs 25). High precision is rarely needed. |
293
+ | `masaSystem` | `'purnimanta' \| 'amanta'` | `'purnimanta'` | Lunar month naming system. Purnimanta (North Indian) or Amanta (South Indian). |
245
294
 
246
295
  **`InstantPanchangOptions`** (optional for `getInstantPanchang`): same as above but without `timezone`.
247
296
 
@@ -249,7 +298,7 @@ console.log(result.panchaka); // false
249
298
 
250
299
  ### Low-level Utilities
251
300
 
252
- These are exported for advanced use cases (building your own tools, visualisations, or debugging).
301
+ Exported for advanced use cases — building custom tools, visualizations, or Jyotish applications.
253
302
 
254
303
  ```typescript
255
304
  import {
@@ -261,7 +310,9 @@ import {
261
310
  computeAbhijitMuhurta, computeBrahmaMuhurta,
262
311
  computeGowriPanchangam,
263
312
  // Jyotish
264
- computePlanetaryPositions, computeVimshottariDasha,
313
+ computePlanetaryPositions,
314
+ computeVimshottariDasha, computeVimshottariDashaFromBirth,
315
+ computeChandraBalam,
265
316
  GRAHA_ABBR,
266
317
  } from 'panchang-ts';
267
318
 
@@ -280,40 +331,48 @@ const sunLon = getSiderealSunLongitude(new Date(), 'lahiri');
280
331
  // Ayanamsa
281
332
  const ayan = getAyanamsa(new Date(), 'lahiri'); // e.g. 24.10
282
333
 
283
- // Inauspicious periods (varaIndex: 0=Sun … 6=Sat)
284
- const rahu = computeRahuKalam(sunrise, sunset, varaIndex); // { start, end }
334
+ // Inauspicious periods (varaIndex: 0=Sun ... 6=Sat)
335
+ const rahu = computeRahuKalam(sunrise, sunset, varaIndex); // { start, end }
285
336
  const gulika = computeGulikaKalam(sunrise, sunset, varaIndex);
286
- const yama = computeYamaganda(sunrise, sunset, varaIndex);
337
+ const yama = computeYamaganda(sunrise, sunset, varaIndex);
287
338
 
288
339
  // Muhurta
289
- const abhijit = computeAbhijitMuhurta(sunrise, sunset); // { start, end }
290
- const brahma = computeBrahmaMuhurta(sunrise, sunset); // { start, end }
291
-
340
+ const abhijit = computeAbhijitMuhurta(sunrise, sunset); // { start, end }
341
+ const brahma = computeBrahmaMuhurta(sunrise, sunset); // { start, end }
342
+ ```
292
343
 
293
- // Gowri Panchangam (varaIndex: 0=Sun … 6=Sat)
294
- const gowri = computeGowriPanchangam(sunrise, sunset, nextSunrise, varaIndex,
295
- (i) => ['Udyog','Amrit','Roga','Laabh','Shubh','Kaal','Dhan','Chal'][i]!,
296
- (q) => ({ auspicious: 'Auspicious', inauspicious: 'Inauspicious', neutral: 'Neutral' })[q],
297
- );
298
- // gowri.day → 8 GowriSlot (sunrise → sunset)
299
- // gowri.night → 8 GowriSlot (sunset → next sunrise)
344
+ **Jyotish (Vedic Astrology):**
300
345
 
301
- // Planetary positions (all 9 grahas, sidereal)
346
+ ```typescript
347
+ // All 9 graha positions (sidereal — Rahu/Ketu use mean node)
302
348
  const grahas = computePlanetaryPositions(birthDate, 'lahiri');
303
- console.log(grahas.jupiter.rashi.name); // e.g. "Dhanu"
304
- console.log(grahas.saturn.isRetrograde); // true/false
305
- console.log(GRAHA_ABBR['Jupiter']); // "Ju"
349
+ console.log(grahas.jupiter.rashi.name); // "Dhanu"
350
+ console.log(grahas.saturn.isRetrograde); // true/false
351
+ console.log(GRAHA_ABBR['Jupiter']); // "Ju"
352
+
353
+ // Vimshottari Dasha — convenience form: birth date only (Moon longitude derived)
354
+ const dasha = computeVimshottariDashaFromBirth(birthDate, 'lahiri');
355
+ console.log(dasha.currentMahaDashaLord); // "Rahu"
356
+ console.log(dasha.mahaDashas[0]!.antarDashas[0]!.lord); // "Rahu"
306
357
 
307
- // Vimshottari Dasha — pass birth date and Moon's sidereal longitude
358
+ // Or pass an explicit Moon sidereal longitude (useful when you already have one)
308
359
  const moonLon = getSiderealMoonLongitude(birthDate, 'lahiri');
309
- const dasha = computeVimshottariDasha(birthDate, moonLon);
310
- console.log(dasha.currentMahaDashaLord); // e.g. "Rahu"
311
- console.log(dasha.mahaDashas[0]!.antarDashas[0]!.lord); // e.g. "Rahu"
360
+ const dasha2 = computeVimshottariDasha(birthDate, moonLon);
361
+
362
+ // Chandra Balam — transit Moon's favorability vs. janma rashi
363
+ // janmaRashi and transitMoonRashi are 0-indexed (0 = Mesha ... 11 = Meena)
364
+ const cb = computeChandraBalam(3 /* Karka */, 6 /* Tula */);
365
+ console.log(cb.house); // 4
366
+ console.log(cb.quality); // "weak"
367
+ console.log(cb.englishName); // "Ashubha"
312
368
  ```
313
369
 
314
370
  ---
315
371
 
316
- ### Types
372
+ ## Types
373
+
374
+ <details>
375
+ <summary><strong>Core Types</strong> — GeoLocation, TimePeriod</summary>
317
376
 
318
377
  ```typescript
319
378
  interface GeoLocation {
@@ -326,45 +385,70 @@ interface TimePeriod {
326
385
  start: Date;
327
386
  end: Date;
328
387
  }
388
+ ```
389
+ </details>
329
390
 
330
- // ── Pancha Anga ──────────────────────────────────────────────────────────────
391
+ <details>
392
+ <summary><strong>Pancha Anga</strong> — TithiInfo, NakshatraInfo, YogaInfo, KaranaInfo, VaraInfo</summary>
331
393
 
394
+ ```typescript
332
395
  interface TithiInfo {
333
- index: number; // 0–29
396
+ index: number; // 0-29
334
397
  name: string; // e.g. "Shukla Pratipada"
335
- paksha: string; // "Shukla"/"Krishna" (en), "शुक्ल"/"कृष्ण" (sa/hi) — localized
336
- number: number; // 1–15 within the paksha
398
+ paksha: string; // "Shukla"/"Krishna" (en), "शुक्ल"/"कृष्ण" (sa/hi)
399
+ number: number; // 1-15 within the paksha
337
400
  completionPercentage: number;
338
401
  endTime: Date | null;
339
402
  }
340
403
 
341
404
  interface NakshatraInfo {
342
- index: number; // 0–26
405
+ index: number; // 0-26
343
406
  name: string;
344
- pada: number; // 1–4
407
+ pada: number; // 1-4
345
408
  degreesInNakshatra: number;
346
409
  completionPercentage: number;
347
410
  endTime: Date | null;
348
411
  }
349
412
 
350
413
  interface DailyTithiInfo extends TithiInfo {
351
- startTime: Date | null; // null when isActiveAtSunrise is true (element was already active at sunrise)
352
- isActiveAtSunrise: boolean; // true → this element was present at sunrise; false → it started mid-day (startTime is set)
414
+ startTime: Date | null; // null when isActiveAtSunrise is true
415
+ isActiveAtSunrise: boolean; // true = present at sunrise; false = started mid-day
353
416
  }
354
417
 
355
418
  // DailyNakshatraInfo, DailyYogaInfo, DailyKaranaInfo follow the same pattern
356
419
 
357
- // Note: endTime and startTime are null on ALL daily elements when computeEndTimes: false.
358
- // completionPercentage: 0 = just started, 100 = about to end (computed at sunrise moment).
420
+ interface VaraInfo {
421
+ index: number; // 0 = Sunday ... 6 = Saturday
422
+ name: string; // e.g. "Ravivara" (localized)
423
+ shortName: string; // e.g. "Ravi" (localized)
424
+ englishName: string; // e.g. "Sunday" (always English)
425
+ }
426
+
427
+ interface KaranaInfo {
428
+ index: number;
429
+ name: string;
430
+ completionPercentage: number;
431
+ endTime: Date | null;
432
+ type: 'fixed' | 'movable';
433
+ }
359
434
 
360
- // ── Lunar calendar ───────────────────────────────────────────────────────────
435
+ // Note: endTime and startTime are null when computeEndTimes: false.
436
+ ```
437
+ </details>
361
438
 
439
+ <details>
440
+ <summary><strong>Lunar Calendar</strong> — ChandraMasaInfo, SamvatInfo, MasaInfo</summary>
441
+
442
+ ```typescript
362
443
  interface ChandraMasaInfo {
363
- index: number; // 0 = Chaitra … 11 = Phalguna (Amanta / South-Indian)
364
- name: string; // e.g. "Pausha" (Amanta name)
365
- isAdhika: boolean; // true = leap/intercalary month
366
- purnimantaIndex: number; // Month index in Purnimanta (North-Indian) system
367
- purnimantaName: string; // Month name in Purnimanta system
444
+ index: number; // 0 = Chaitra ... 11 = Phalguna (in the active system)
445
+ name: string; // follows masaSystem option
446
+ isAdhika: boolean; // true = leap/intercalary month
447
+ system: 'purnimanta' | 'amanta';
448
+ amantaIndex: number; // month index in Amanta system
449
+ amantaName: string; // month name in Amanta system
450
+ purnimantaIndex: number; // month index in Purnimanta system
451
+ purnimantaName: string; // month name in Purnimanta system
368
452
  }
369
453
 
370
454
  interface SamvatInfo {
@@ -372,101 +456,83 @@ interface SamvatInfo {
372
456
  shakaSamvat: number; // e.g. 1946
373
457
  }
374
458
 
375
- // ── Zodiac & asterism ────────────────────────────────────────────────────────
376
-
377
- interface RashiInfo {
378
- index: number; // 0 = Mesha … 11 = Meena (for rashi); 0–26 for nakshatra
379
- name: string;
380
- }
381
-
382
- // chandraRashi and suryaNakshatra both use RashiInfo
383
-
384
- interface VaraInfo {
385
- index: number; // 0 = Sunday … 6 = Saturday
386
- name: string; // e.g. "Ravivara" (localized full name)
387
- shortName: string; // e.g. "Ravi" (localized short form, Sanskrit by default)
388
- englishName: string; // e.g. "Sunday" (always English, locale-independent)
389
- }
390
-
391
459
  interface MasaInfo {
392
- index: number; // 0 = Mesha … 11 = Meena (solar month)
393
- name: string; // e.g. "Dhanu"
460
+ index: number; // 0 = Mesha ... 11 = Meena (solar month)
461
+ name: string;
394
462
  }
395
463
 
396
- interface KaranaInfo {
397
- index: number;
464
+ interface RashiInfo {
465
+ index: number; // 0 = Mesha ... 11 = Meena
398
466
  name: string;
399
- completionPercentage: number;
400
- endTime: Date | null;
401
- type: 'fixed' | 'movable';
402
467
  }
468
+ ```
469
+ </details>
403
470
 
404
- // ── Choghadiya ───────────────────────────────────────────────────────────────
471
+ <details>
472
+ <summary><strong>Time Slots</strong> — Choghadiya, Gowri Panchangam, Hora</summary>
405
473
 
474
+ ```typescript
406
475
  type ChoghadiyaQuality = 'auspicious' | 'inauspicious' | 'neutral';
407
476
 
408
477
  interface ChoghadiyaSlot extends TimePeriod {
409
478
  index: number;
410
- name: string; // e.g. "Amrit", "Kaal", "Shubh" (localized)
411
- quality: ChoghadiyaQuality; // programmatic key: 'auspicious' | 'inauspicious' | 'neutral'
412
- qualityName: string; // localized display name (e.g. "Auspicious", "शुभ", "शुभम्")
479
+ name: string; // e.g. "Amrit", "Kaal" (localized)
480
+ quality: ChoghadiyaQuality;
481
+ qualityName: string; // localized: "Auspicious", "शुभ", "शुभम्"
413
482
  }
414
483
 
415
484
  interface ChoghadiyaInfo {
416
- day: ChoghadiyaSlot[]; // 8 slots (sunrise → sunset)
417
- night: ChoghadiyaSlot[]; // 8 slots (sunset → next sunrise)
485
+ day: ChoghadiyaSlot[]; // 8 slots (sunrise -> sunset)
486
+ night: ChoghadiyaSlot[]; // 8 slots (sunset -> next sunrise)
418
487
  }
419
488
 
420
- // ── Gowri Panchangam ─────────────────────────────────────────────────────────
421
-
422
489
  interface GowriSlot extends TimePeriod {
423
- index: number; // 0–7 within the 8-name cycle
424
- name: string; // e.g. "Amrit", "Kaal", "Shubh" (localized)
425
- quality: ChoghadiyaQuality; // programmatic key: 'auspicious' | 'inauspicious' | 'neutral'
426
- qualityName: string; // localized display name (e.g. "Auspicious", "शुभ", "शुभम्")
490
+ index: number; // 0-7 within the 8-name cycle
491
+ name: string; // e.g. "Amrit", "Kaal" (localized)
492
+ quality: ChoghadiyaQuality;
493
+ qualityName: string;
427
494
  }
428
495
 
429
496
  interface GowriInfo {
430
- day: GowriSlot[]; // 8 slots (sunrise → sunset)
431
- night: GowriSlot[]; // 8 slots (sunset → next sunrise)
497
+ day: GowriSlot[]; // 8 slots (sunrise -> sunset)
498
+ night: GowriSlot[]; // 8 slots (sunset -> next sunrise)
432
499
  }
433
500
 
434
- // ── Hora ─────────────────────────────────────────────────────────────────────
435
-
436
501
  interface HoraSlot extends TimePeriod {
437
- planet: string; // Chaldean order: "Sun"(0), "Venus"(1), "Mercury"(2), "Moon"(3), "Saturn"(4), "Jupiter"(5), "Mars"(6)
438
- planetIndex: number; // 0–6 — index into the Chaldean sequence above
502
+ planet: string; // e.g. "Sun", "Venus", "Mercury"
503
+ planetIndex: number; // 0-6 in Chaldean order
439
504
  }
440
505
 
441
506
  interface HoraInfo {
442
- day: HoraSlot[]; // 12 slots (sunrise → sunset)
443
- night: HoraSlot[]; // 12 slots (sunset → next sunrise)
507
+ day: HoraSlot[]; // 12 slots (sunrise -> sunset)
508
+ night: HoraSlot[]; // 12 slots (sunset -> next sunrise)
444
509
  }
510
+ ```
511
+ </details>
445
512
 
446
- // ── Special Yogas ────────────────────────────────────────────────────────────
513
+ <details>
514
+ <summary><strong>Special Yogas & Festivals</strong></summary>
447
515
 
516
+ ```typescript
448
517
  interface SpecialYogaInfo {
449
518
  name: string; // e.g. "Guru Pushya Yoga"
450
519
  type: 'amrit_siddhi' | 'sarvartha_siddhi' | 'ravi_pushya' | 'guru_pushya';
451
520
  }
452
521
 
453
- // ── Festivals ────────────────────────────────────────────────────────────────
454
-
455
522
  interface FestivalInfo {
456
523
  name: string; // e.g. "Diwali", "Ekadashi"
457
524
  type: 'major' | 'minor' | 'ekadashi' | 'pradosha' | 'sankranti';
458
- description?: string; // For sankranti: localized rashi name (e.g. "Makara", "मकर")
525
+ description?: string; // For sankranti: localized rashi name
459
526
  }
527
+ ```
528
+ </details>
460
529
 
461
- // Detection rules:
462
- // Ekadashi: both Shukla (tithi 10) and Krishna (tithi 25) pakshas
463
- // Pradosha: Krishna Trayodashi only (tithi 27)
464
- // Sankranti: Sun within 1° past a rashi boundary (degInRashi < 1.0)
465
- // Fixed festivals (e.g. Diwali): matched by chandramasa index + tithi index; skipped during Adhika months
466
-
467
- // ── Jyotish (Vedic astrology) ────────────────────────────────────────────────
530
+ <details>
531
+ <summary><strong>Jyotish (Vedic Astrology)</strong> — Graha positions, Vimshottari Dasha</summary>
468
532
 
469
- type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter' | 'Venus' | 'Saturn' | 'Rahu' | 'Ketu';
533
+ ```typescript
534
+ type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter'
535
+ | 'Venus' | 'Saturn' | 'Rahu' | 'Ketu';
470
536
 
471
537
  interface GrahaPosition {
472
538
  planet: GrahaName;
@@ -483,7 +549,8 @@ interface PlanetaryPositions {
483
549
  saturn: GrahaPosition; rahu: GrahaPosition; ketu: GrahaPosition;
484
550
  }
485
551
 
486
- type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars' | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
552
+ type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars'
553
+ | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
487
554
 
488
555
  interface AntarDasha {
489
556
  lord: DashaLord;
@@ -495,21 +562,28 @@ interface MahaDasha {
495
562
  lord: DashaLord;
496
563
  startDate: Date;
497
564
  endDate: Date;
498
- years: number; // full duration in years (proportional for the first/partial dasha)
565
+ years: number;
499
566
  antarDashas: AntarDasha[];
500
567
  }
501
568
 
502
569
  interface VimshottariDashaResult {
503
- currentMahaDashaLord: DashaLord; // active Mahadasha as of today
504
- currentIndex: number; // index into mahaDashas
505
- mahaDashas: MahaDasha[]; // 9-entry sequence starting from birth
570
+ currentMahaDashaLord: DashaLord;
571
+ currentIndex: number;
572
+ mahaDashas: MahaDasha[]; // 9-entry sequence starting from birth
506
573
  }
507
574
 
575
+ interface ChandraBalamInfo {
576
+ house: number; // 1 = janma rashi; 12 = rashi before janma
577
+ quality: 'strong' | 'weak'; // Shubha houses = 1,3,6,7,10,11
578
+ englishName: string; // "Shubha" | "Ashubha"
579
+ name: string; // localized
580
+ }
508
581
  ```
582
+ </details>
509
583
 
510
584
  ---
511
585
 
512
- ## React Native / Hermes Usage
586
+ ## React Native / Hermes
513
587
 
514
588
  Works with Expo and bare React Native (Hermes engine). Pass `timezone` as a **number**
515
589
  — IANA timezone strings (`'Asia/Kolkata'`) require `Intl`, which older Hermes versions
@@ -537,57 +611,53 @@ InteractionManager.runAfterInteractions(() => {
537
611
 
538
612
  ---
539
613
 
540
- ## Performance
541
-
542
- | Mode | Node.js | Hermes (budget Android) |
543
- |------|---------|------------------------|
544
- | Names-only (`computeEndTimes: false`) | ~0.1 ms | <100 ms |
545
- | Full with end-times | ~0.5 ms | <500 ms |
546
-
547
- Measured with Vitest benchmarks on Node 22 and on a physical budget Android device
548
- via the dharmSetu React Native app.
549
-
550
- ---
551
-
552
614
  ## Accuracy
553
615
 
554
- Validated against [DrikPanchang.com](https://www.drikpanchang.com) for 19+ date/city combinations (Pune, Delhi, Chennai, Mumbai, Bangalore, New York).
555
-
556
- | Element | Accuracy |
557
- |---------|----------|
558
- | Sunrise / Sunset | ±2 minutes |
559
- | Moonrise / Moonset | ±2 minutes |
560
- | Tithi, Nakshatra, Yoga, Karana names | Exact match |
561
- | Element end-times | ±5 minutes |
562
- | Ayanamsa | ±0.005° vs Swiss Ephemeris |
563
- | Choghadiya / Hora / Gowri slots | Derived from sunrise/sunset — inherits ±2 min |
564
-
565
- ### Validation test suite
566
-
567
- The test suite includes:
568
- - **242-day structural regression** (Pune, Sep 2025 – Apr 2026): verifies no crash, correct Vara, time-ordering invariants, element counts, and all fields (including Gowri Panchangam) for every day in the window
569
- - **19 precise-value tests** across 5 Indian cities + New York: exact Tithi name, Nakshatra name, sunrise/sunset HH:MM (±2 min tolerance), Chandra Masa name
570
- - **10 long-range regression tests** (2030–2050): structural correctness and ayanamsa bounds for future dates
571
-
572
- To generate fixture stubs for new date ranges (e.g. to populate against DrikPanchang):
573
-
574
- ```bash
575
- npx tsx scripts/generate-fixtures.ts --start 2027-01-01 --end 2027-03-31 --city Delhi
576
- ```
616
+ 4,864 tests passing, including Drik-verified fixtures against
617
+ [DrikPanchang.com](https://www.drikpanchang.com) spanning 2025–2026 across
618
+ Delhi, Chennai, and New York.
619
+
620
+ | Element | Accuracy | Validation |
621
+ |---------|----------|------------|
622
+ | Sunrise / Sunset | **≤29 s observed vs Drik minute-midpoint** (±45 s tolerance) | 16 assertions |
623
+ | Moonrise / Moonset | ±2 min vs Drik | Strict fixtures |
624
+ | Tithi, Nakshatra, Yoga, Karana names | Exact match vs Drik | Strict fixtures |
625
+ | Tithi / Nakshatra / Yoga / Karana end-times | **±3 min tolerance, max 2.01 min observed** | 20 assertions |
626
+ | Ayanamsa | ±0.005° vs Swiss Ephemeris | Unit tests |
627
+ | Planetary positions (Sun–Saturn) | **±0.02° vs Drik sidereal** | Drik fixtures |
628
+ | Planetary positions (Rahu/Ketu, mean node) | ≤0.5° typical; ±2° tolerance to absorb mean-vs-true drift | Drik fixtures |
629
+ | Rashi / Nakshatra / Retrograde flag | Exact match vs Drik | Drik fixtures |
630
+ | Festival dates | 12 Drik-verified festivals (2025–2026) — see caveats below | Drik fixtures |
631
+ | Choghadiya / Hora / Gowri slots | Derived from sunrise/sunset — inherits ±2 min | — |
632
+
633
+ ### Festival Detection — Documented Tradeoff
634
+
635
+ Library uses **tithi-at-sunrise** to resolve a festival to a calendar day.
636
+ DrikPanchang applies several other traditional rules depending on the
637
+ festival; where those rules pick a different day, our output can drift
638
+ ±1 day vs Drik. This is a rule-choice tradeoff, not a computation bug —
639
+ it is documented and deliberately surfaced rather than hidden.
640
+
641
+ | Resolution rule Drik uses | Festivals affected |
642
+ |---------------------------|--------------------|
643
+ | Tithi-at-midnight | Krishna Janmashtami, Maha Shivaratri, Diwali / Lakshmi Puja |
644
+ | Madhyahna-vyapini (tithi overlapping noon) | Ganesh Chaturthi on edge years, Akshaya Tritiya 2026 |
645
+ | Kshaya-tithi handling (tithi never at sunrise) | Ugadi 2026-03-19 (Pratipad is Kshaya) |
646
+
647
+ If exact Drik parity matters for your use case, cross-check the above
648
+ festival set against the Drik site for the target year. Everything else
649
+ — Holi, Ugadi (non-Kshaya years), Rama Navami, Raksha Bandhan, Ganesh
650
+ Chaturthi (normal years), Navaratri, Dussehra, Karva Chauth, Hanuman
651
+ Jayanti — matches Drik's canonical date across 2025 and 2026 fixtures.
577
652
 
578
653
  ---
579
654
 
580
- ## Compatibility
655
+ ## Performance
581
656
 
582
- | Environment | Support |
583
- |-------------|---------|
584
- | Node.js 18+ | ✅ |
585
- | Node.js 20+ | ✅ |
586
- | Node.js 22+ | ✅ |
587
- | React Native (Hermes) | ✅ (pass `timezone` as number) |
588
- | Expo (managed + bare) | ✅ |
589
- | Browser (modern) | ✅ (ESM build) |
590
- | Browser (legacy / IE) | ✗ |
657
+ | Mode | Node.js | Hermes (budget Android) |
658
+ |------|---------|------------------------|
659
+ | Names-only (`computeEndTimes: false`) | ~0.1 ms | <100 ms |
660
+ | Full with end-times | ~0.5 ms | <500 ms |
591
661
 
592
662
  ---
593
663
 
@@ -606,21 +676,35 @@ try {
606
676
  }
607
677
  ```
608
678
 
609
- `PanchangErrorCode` values: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
679
+ Error codes: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
610
680
  `INVALID_ELEVATION`, `INVALID_TIMEZONE`, `INVALID_AYANAMSA`, `TIMEZONE_RESOLUTION_FAILED`,
611
681
  `NO_SUNRISE`, `NO_SUNSET`, `SEARCH_DIVERGED`.
612
682
 
613
- Note: `getMoonrise` / `getMoonset` never throw — they return `null` when no rise/set
614
- occurs within the search window (this is normal for the Moon).
683
+ `getMoonrise` / `getMoonset` return `null` instead of throwing when no rise/set
684
+ occurs (normal for the Moon).
615
685
 
616
686
  ---
617
687
 
618
- ## Acknowledgements
688
+ ## Compatibility
619
689
 
620
- - **[astronomy-engine](https://github.com/cosinekitty/astronomy)** by Don Cross — the sole runtime dependency. Provides the astronomical algorithms used for sunrise/sunset, moonrise/moonset, and planetary longitude calculations. MIT licensed.
690
+ | Environment | Support |
691
+ |-------------|---------|
692
+ | Node.js 18+ | Supported |
693
+ | React Native (Hermes) | Supported (pass `timezone` as number) |
694
+ | Expo (managed + bare) | Supported |
695
+ | Browser (modern) | Supported (ESM build) |
696
+ | Browser (legacy / IE) | Not supported |
621
697
 
622
698
  ---
623
699
 
700
+ ## Used By
701
+
702
+ - [dharmagya.app](https://dharmagya.app) — Daily Panchang and Hindu calendar
703
+
704
+ ## Acknowledgements
705
+
706
+ [astronomy-engine](https://github.com/cosinekitty/astronomy) by Don Cross — the sole runtime dependency. MIT licensed.
707
+
624
708
  ## License
625
709
 
626
710
  MIT