panchang-ts 0.6.0 → 0.7.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,45 +67,35 @@ 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
 
73
74
  // Choghadiya — 8 daytime slots
74
75
  result.choghadiya.day.forEach(slot => {
75
- console.log(slot.name, slot.quality); // "Amrit", "auspicious"
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.quality); // "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.
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 12) |
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 24) |
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 ~1314 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 ~ 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 {
@@ -280,39 +329,38 @@ const sunLon = getSiderealSunLongitude(new Date(), 'lahiri');
280
329
  // Ayanamsa
281
330
  const ayan = getAyanamsa(new Date(), 'lahiri'); // e.g. 24.10
282
331
 
283
- // Inauspicious periods (varaIndex: 0=Sun 6=Sat)
284
- const rahu = computeRahuKalam(sunrise, sunset, varaIndex); // { start, end }
332
+ // Inauspicious periods (varaIndex: 0=Sun ... 6=Sat)
333
+ const rahu = computeRahuKalam(sunrise, sunset, varaIndex); // { start, end }
285
334
  const gulika = computeGulikaKalam(sunrise, sunset, varaIndex);
286
- const yama = computeYamaganda(sunrise, sunset, varaIndex);
335
+ const yama = computeYamaganda(sunrise, sunset, varaIndex);
287
336
 
288
337
  // Muhurta
289
- const abhijit = computeAbhijitMuhurta(sunrise, sunset); // { start, end }
290
- const brahma = computeBrahmaMuhurta(sunrise, sunset); // { start, end }
291
-
338
+ const abhijit = computeAbhijitMuhurta(sunrise, sunset); // { start, end }
339
+ const brahma = computeBrahmaMuhurta(sunrise, sunset); // { start, end }
340
+ ```
292
341
 
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
- );
297
- // gowri.day → 8 GowriSlot (sunrise → sunset)
298
- // gowri.night → 8 GowriSlot (sunset → next sunrise)
342
+ **Jyotish (Vedic Astrology):**
299
343
 
300
- // Planetary positions (all 9 grahas, sidereal)
344
+ ```typescript
345
+ // All 9 graha positions (sidereal)
301
346
  const grahas = computePlanetaryPositions(birthDate, 'lahiri');
302
- console.log(grahas.jupiter.rashi.name); // e.g. "Dhanu"
303
- console.log(grahas.saturn.isRetrograde); // true/false
304
- console.log(GRAHA_ABBR['Jupiter']); // "Ju"
347
+ console.log(grahas.jupiter.rashi.name); // "Dhanu"
348
+ console.log(grahas.saturn.isRetrograde); // true/false
349
+ console.log(GRAHA_ABBR['Jupiter']); // "Ju"
305
350
 
306
351
  // Vimshottari Dasha — pass birth date and Moon's sidereal longitude
307
352
  const moonLon = getSiderealMoonLongitude(birthDate, 'lahiri');
308
353
  const dasha = computeVimshottariDasha(birthDate, moonLon);
309
- console.log(dasha.currentMahaDashaLord); // e.g. "Rahu"
310
- console.log(dasha.mahaDashas[0]!.antarDashas[0]!.lord); // e.g. "Rahu"
354
+ console.log(dasha.currentMahaDashaLord); // "Rahu"
355
+ console.log(dasha.mahaDashas[0]!.antarDashas[0]!.lord); // "Rahu"
311
356
  ```
312
357
 
313
358
  ---
314
359
 
315
- ### Types
360
+ ## Types
361
+
362
+ <details>
363
+ <summary><strong>Core Types</strong> — GeoLocation, TimePeriod</summary>
316
364
 
317
365
  ```typescript
318
366
  interface GeoLocation {
@@ -325,45 +373,70 @@ interface TimePeriod {
325
373
  start: Date;
326
374
  end: Date;
327
375
  }
376
+ ```
377
+ </details>
328
378
 
329
- // ── Pancha Anga ──────────────────────────────────────────────────────────────
379
+ <details>
380
+ <summary><strong>Pancha Anga</strong> — TithiInfo, NakshatraInfo, YogaInfo, KaranaInfo, VaraInfo</summary>
330
381
 
382
+ ```typescript
331
383
  interface TithiInfo {
332
- index: number; // 029
384
+ index: number; // 0-29
333
385
  name: string; // e.g. "Shukla Pratipada"
334
- paksha: string; // "Shukla"/"Krishna" (en), "शुक्ल"/"कृष्ण" (sa/hi) — localized
335
- number: number; // 115 within the paksha
386
+ paksha: string; // "Shukla"/"Krishna" (en), "शुक्ल"/"कृष्ण" (sa/hi)
387
+ number: number; // 1-15 within the paksha
336
388
  completionPercentage: number;
337
389
  endTime: Date | null;
338
390
  }
339
391
 
340
392
  interface NakshatraInfo {
341
- index: number; // 026
393
+ index: number; // 0-26
342
394
  name: string;
343
- pada: number; // 14
395
+ pada: number; // 1-4
344
396
  degreesInNakshatra: number;
345
397
  completionPercentage: number;
346
398
  endTime: Date | null;
347
399
  }
348
400
 
349
401
  interface DailyTithiInfo extends TithiInfo {
350
- startTime: Date | null; // null when isActiveAtSunrise is true (element was already active at sunrise)
351
- isActiveAtSunrise: boolean; // true this element was present at sunrise; false it started mid-day (startTime is set)
402
+ startTime: Date | null; // null when isActiveAtSunrise is true
403
+ isActiveAtSunrise: boolean; // true = present at sunrise; false = started mid-day
352
404
  }
353
405
 
354
406
  // DailyNakshatraInfo, DailyYogaInfo, DailyKaranaInfo follow the same pattern
355
407
 
356
- // Note: endTime and startTime are null on ALL daily elements when computeEndTimes: false.
357
- // completionPercentage: 0 = just started, 100 = about to end (computed at sunrise moment).
408
+ interface VaraInfo {
409
+ index: number; // 0 = Sunday ... 6 = Saturday
410
+ name: string; // e.g. "Ravivara" (localized)
411
+ shortName: string; // e.g. "Ravi" (localized)
412
+ englishName: string; // e.g. "Sunday" (always English)
413
+ }
414
+
415
+ interface KaranaInfo {
416
+ index: number;
417
+ name: string;
418
+ completionPercentage: number;
419
+ endTime: Date | null;
420
+ type: 'fixed' | 'movable';
421
+ }
422
+
423
+ // Note: endTime and startTime are null when computeEndTimes: false.
424
+ ```
425
+ </details>
358
426
 
359
- // ── Lunar calendar ───────────────────────────────────────────────────────────
427
+ <details>
428
+ <summary><strong>Lunar Calendar</strong> — ChandraMasaInfo, SamvatInfo, MasaInfo</summary>
360
429
 
430
+ ```typescript
361
431
  interface ChandraMasaInfo {
362
- index: number; // 0 = Chaitra 11 = Phalguna (Amanta / South-Indian)
363
- name: string; // e.g. "Pausha" (Amanta name)
364
- isAdhika: boolean; // true = leap/intercalary month
365
- purnimantaIndex: number; // Month index in Purnimanta (North-Indian) system
366
- purnimantaName: string; // Month name in Purnimanta system
432
+ index: number; // 0 = Chaitra ... 11 = Phalguna (in the active system)
433
+ name: string; // follows masaSystem option
434
+ isAdhika: boolean; // true = leap/intercalary month
435
+ system: 'purnimanta' | 'amanta';
436
+ amantaIndex: number; // month index in Amanta system
437
+ amantaName: string; // month name in Amanta system
438
+ purnimantaIndex: number; // month index in Purnimanta system
439
+ purnimantaName: string; // month name in Purnimanta system
367
440
  }
368
441
 
369
442
  interface SamvatInfo {
@@ -371,99 +444,83 @@ interface SamvatInfo {
371
444
  shakaSamvat: number; // e.g. 1946
372
445
  }
373
446
 
374
- // ── Zodiac & asterism ────────────────────────────────────────────────────────
375
-
376
- interface RashiInfo {
377
- index: number; // 0 = Mesha … 11 = Meena (for rashi); 0–26 for nakshatra
378
- name: string;
379
- }
380
-
381
- // chandraRashi and suryaNakshatra both use RashiInfo
382
-
383
- interface VaraInfo {
384
- index: number; // 0 = Sunday … 6 = Saturday
385
- name: string; // e.g. "Ravivara" (localized full name)
386
- shortName: string; // e.g. "Ravi" (localized short form, Sanskrit by default)
387
- englishName: string; // e.g. "Sunday" (always English, locale-independent)
388
- }
389
-
390
447
  interface MasaInfo {
391
- index: number; // 0 = Mesha 11 = Meena (solar month)
392
- name: string; // e.g. "Dhanu"
448
+ index: number; // 0 = Mesha ... 11 = Meena (solar month)
449
+ name: string;
393
450
  }
394
451
 
395
- interface KaranaInfo {
396
- index: number;
452
+ interface RashiInfo {
453
+ index: number; // 0 = Mesha ... 11 = Meena
397
454
  name: string;
398
- completionPercentage: number;
399
- endTime: Date | null;
400
- type: 'fixed' | 'movable';
401
455
  }
456
+ ```
457
+ </details>
402
458
 
403
- // ── Choghadiya ───────────────────────────────────────────────────────────────
459
+ <details>
460
+ <summary><strong>Time Slots</strong> — Choghadiya, Gowri Panchangam, Hora</summary>
404
461
 
462
+ ```typescript
405
463
  type ChoghadiyaQuality = 'auspicious' | 'inauspicious' | 'neutral';
406
464
 
407
465
  interface ChoghadiyaSlot extends TimePeriod {
408
466
  index: number;
409
- name: string; // e.g. "Amrit", "Kaal", "Shubh"
467
+ name: string; // e.g. "Amrit", "Kaal" (localized)
410
468
  quality: ChoghadiyaQuality;
469
+ qualityName: string; // localized: "Auspicious", "शुभ", "शुभम्"
411
470
  }
412
471
 
413
472
  interface ChoghadiyaInfo {
414
- day: ChoghadiyaSlot[]; // 8 slots (sunrise sunset)
415
- night: ChoghadiyaSlot[]; // 8 slots (sunset next sunrise)
473
+ day: ChoghadiyaSlot[]; // 8 slots (sunrise -> sunset)
474
+ night: ChoghadiyaSlot[]; // 8 slots (sunset -> next sunrise)
416
475
  }
417
476
 
418
- // ── Gowri Panchangam ─────────────────────────────────────────────────────────
419
-
420
477
  interface GowriSlot extends TimePeriod {
421
- index: number; // 07 within the 8-name cycle
422
- name: string; // e.g. "Amrit", "Kaal", "Shubh"
478
+ index: number; // 0-7 within the 8-name cycle
479
+ name: string; // e.g. "Amrit", "Kaal" (localized)
423
480
  quality: ChoghadiyaQuality;
481
+ qualityName: string;
424
482
  }
425
483
 
426
484
  interface GowriInfo {
427
- day: GowriSlot[]; // 8 slots (sunrise sunset)
428
- night: GowriSlot[]; // 8 slots (sunset next sunrise)
485
+ day: GowriSlot[]; // 8 slots (sunrise -> sunset)
486
+ night: GowriSlot[]; // 8 slots (sunset -> next sunrise)
429
487
  }
430
488
 
431
- // ── Hora ─────────────────────────────────────────────────────────────────────
432
-
433
489
  interface HoraSlot extends TimePeriod {
434
- planet: string; // Chaldean order: "Sun"(0), "Venus"(1), "Mercury"(2), "Moon"(3), "Saturn"(4), "Jupiter"(5), "Mars"(6)
435
- planetIndex: number; // 06 index into the Chaldean sequence above
490
+ planet: string; // e.g. "Sun", "Venus", "Mercury"
491
+ planetIndex: number; // 0-6 in Chaldean order
436
492
  }
437
493
 
438
494
  interface HoraInfo {
439
- day: HoraSlot[]; // 12 slots (sunrise sunset)
440
- night: HoraSlot[]; // 12 slots (sunset next sunrise)
495
+ day: HoraSlot[]; // 12 slots (sunrise -> sunset)
496
+ night: HoraSlot[]; // 12 slots (sunset -> next sunrise)
441
497
  }
498
+ ```
499
+ </details>
442
500
 
443
- // ── Special Yogas ────────────────────────────────────────────────────────────
501
+ <details>
502
+ <summary><strong>Special Yogas & Festivals</strong></summary>
444
503
 
504
+ ```typescript
445
505
  interface SpecialYogaInfo {
446
506
  name: string; // e.g. "Guru Pushya Yoga"
447
507
  type: 'amrit_siddhi' | 'sarvartha_siddhi' | 'ravi_pushya' | 'guru_pushya';
448
508
  }
449
509
 
450
- // ── Festivals ────────────────────────────────────────────────────────────────
451
-
452
510
  interface FestivalInfo {
453
511
  name: string; // e.g. "Diwali", "Ekadashi"
454
512
  type: 'major' | 'minor' | 'ekadashi' | 'pradosha' | 'sankranti';
455
- description?: string; // For sankranti: "Rashi 3" — the numeric rashi index as a string; look up name from masaNames
513
+ description?: string; // For sankranti: localized rashi name
456
514
  }
515
+ ```
516
+ </details>
457
517
 
458
- // Detection rules:
459
- // Ekadashi: both Shukla (tithi 10) and Krishna (tithi 25) pakshas
460
- // Pradosha: Krishna Trayodashi only (tithi 27)
461
- // Sankranti: Sun within 1° past a rashi boundary (degInRashi < 1.0)
462
- // Fixed festivals (e.g. Diwali): matched by chandramasa index + tithi index; skipped during Adhika months
463
-
464
- // ── Jyotish (Vedic astrology) ────────────────────────────────────────────────
518
+ <details>
519
+ <summary><strong>Jyotish (Vedic Astrology)</strong> Graha positions, Vimshottari Dasha</summary>
465
520
 
466
- type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter' | 'Venus' | 'Saturn' | 'Rahu' | 'Ketu';
521
+ ```typescript
522
+ type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter'
523
+ | 'Venus' | 'Saturn' | 'Rahu' | 'Ketu';
467
524
 
468
525
  interface GrahaPosition {
469
526
  planet: GrahaName;
@@ -480,7 +537,8 @@ interface PlanetaryPositions {
480
537
  saturn: GrahaPosition; rahu: GrahaPosition; ketu: GrahaPosition;
481
538
  }
482
539
 
483
- type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars' | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
540
+ type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars'
541
+ | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
484
542
 
485
543
  interface AntarDasha {
486
544
  lord: DashaLord;
@@ -492,21 +550,21 @@ interface MahaDasha {
492
550
  lord: DashaLord;
493
551
  startDate: Date;
494
552
  endDate: Date;
495
- years: number; // full duration in years (proportional for the first/partial dasha)
553
+ years: number;
496
554
  antarDashas: AntarDasha[];
497
555
  }
498
556
 
499
557
  interface VimshottariDashaResult {
500
- currentMahaDashaLord: DashaLord; // active Mahadasha as of today
501
- currentIndex: number; // index into mahaDashas
502
- mahaDashas: MahaDasha[]; // 9-entry sequence starting from birth
558
+ currentMahaDashaLord: DashaLord;
559
+ currentIndex: number;
560
+ mahaDashas: MahaDasha[]; // 9-entry sequence starting from birth
503
561
  }
504
-
505
562
  ```
563
+ </details>
506
564
 
507
565
  ---
508
566
 
509
- ## React Native / Hermes Usage
567
+ ## React Native / Hermes
510
568
 
511
569
  Works with Expo and bare React Native (Hermes engine). Pass `timezone` as a **number**
512
570
  — IANA timezone strings (`'Asia/Kolkata'`) require `Intl`, which older Hermes versions
@@ -534,21 +592,9 @@ InteractionManager.runAfterInteractions(() => {
534
592
 
535
593
  ---
536
594
 
537
- ## Performance
538
-
539
- | Mode | Node.js | Hermes (budget Android) |
540
- |------|---------|------------------------|
541
- | Names-only (`computeEndTimes: false`) | ~0.1 ms | <100 ms |
542
- | Full with end-times | ~0.5 ms | <500 ms |
543
-
544
- Measured with Vitest benchmarks on Node 22 and on a physical budget Android device
545
- via the dharmSetu React Native app.
546
-
547
- ---
548
-
549
595
  ## Accuracy
550
596
 
551
- Validated against [DrikPanchang.com](https://www.drikpanchang.com) for 19+ date/city combinations (Pune, Delhi, Chennai, Mumbai, Bangalore, New York).
597
+ Validated against [DrikPanchang.com](https://www.drikpanchang.com) for 19+ date/city combinations across India and New York.
552
598
 
553
599
  | Element | Accuracy |
554
600
  |---------|----------|
@@ -559,32 +605,14 @@ Validated against [DrikPanchang.com](https://www.drikpanchang.com) for 19+ date/
559
605
  | Ayanamsa | ±0.005° vs Swiss Ephemeris |
560
606
  | Choghadiya / Hora / Gowri slots | Derived from sunrise/sunset — inherits ±2 min |
561
607
 
562
- ### Validation test suite
563
-
564
- The test suite includes:
565
- - **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
566
- - **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
567
- - **10 long-range regression tests** (2030–2050): structural correctness and ayanamsa bounds for future dates
568
-
569
- To generate fixture stubs for new date ranges (e.g. to populate against DrikPanchang):
570
-
571
- ```bash
572
- npx tsx scripts/generate-fixtures.ts --start 2027-01-01 --end 2027-03-31 --city Delhi
573
- ```
574
-
575
608
  ---
576
609
 
577
- ## Compatibility
610
+ ## Performance
578
611
 
579
- | Environment | Support |
580
- |-------------|---------|
581
- | Node.js 18+ | |
582
- | Node.js 20+ | |
583
- | Node.js 22+ | ✅ |
584
- | React Native (Hermes) | ✅ (pass `timezone` as number) |
585
- | Expo (managed + bare) | ✅ |
586
- | Browser (modern) | ✅ (ESM build) |
587
- | Browser (legacy / IE) | ✗ |
612
+ | Mode | Node.js | Hermes (budget Android) |
613
+ |------|---------|------------------------|
614
+ | Names-only (`computeEndTimes: false`) | ~0.1 ms | <100 ms |
615
+ | Full with end-times | ~0.5 ms | <500 ms |
588
616
 
589
617
  ---
590
618
 
@@ -603,21 +631,35 @@ try {
603
631
  }
604
632
  ```
605
633
 
606
- `PanchangErrorCode` values: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
634
+ Error codes: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
607
635
  `INVALID_ELEVATION`, `INVALID_TIMEZONE`, `INVALID_AYANAMSA`, `TIMEZONE_RESOLUTION_FAILED`,
608
636
  `NO_SUNRISE`, `NO_SUNSET`, `SEARCH_DIVERGED`.
609
637
 
610
- Note: `getMoonrise` / `getMoonset` never throw — they return `null` when no rise/set
611
- occurs within the search window (this is normal for the Moon).
638
+ `getMoonrise` / `getMoonset` return `null` instead of throwing when no rise/set
639
+ occurs (normal for the Moon).
612
640
 
613
641
  ---
614
642
 
615
- ## Acknowledgements
643
+ ## Compatibility
616
644
 
617
- - **[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.
645
+ | Environment | Support |
646
+ |-------------|---------|
647
+ | Node.js 18+ | Supported |
648
+ | React Native (Hermes) | Supported (pass `timezone` as number) |
649
+ | Expo (managed + bare) | Supported |
650
+ | Browser (modern) | Supported (ESM build) |
651
+ | Browser (legacy / IE) | Not supported |
618
652
 
619
653
  ---
620
654
 
655
+ ## Used By
656
+
657
+ - [dharmagya.app](https://dharmagya.app) — Daily Panchang and Hindu calendar
658
+
659
+ ## Acknowledgements
660
+
661
+ [astronomy-engine](https://github.com/cosinekitty/astronomy) by Don Cross — the sole runtime dependency. MIT licensed.
662
+
621
663
  ## License
622
664
 
623
665
  MIT