panchang-ts 0.6.1 → 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,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.
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,40 +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
- (q) => ({ auspicious: 'Auspicious', inauspicious: 'Inauspicious', neutral: 'Neutral' })[q],
297
- );
298
- // gowri.day → 8 GowriSlot (sunrise → sunset)
299
- // gowri.night → 8 GowriSlot (sunset → next sunrise)
342
+ **Jyotish (Vedic Astrology):**
300
343
 
301
- // Planetary positions (all 9 grahas, sidereal)
344
+ ```typescript
345
+ // All 9 graha positions (sidereal)
302
346
  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"
347
+ console.log(grahas.jupiter.rashi.name); // "Dhanu"
348
+ console.log(grahas.saturn.isRetrograde); // true/false
349
+ console.log(GRAHA_ABBR['Jupiter']); // "Ju"
306
350
 
307
351
  // Vimshottari Dasha — pass birth date and Moon's sidereal longitude
308
352
  const moonLon = getSiderealMoonLongitude(birthDate, 'lahiri');
309
353
  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"
354
+ console.log(dasha.currentMahaDashaLord); // "Rahu"
355
+ console.log(dasha.mahaDashas[0]!.antarDashas[0]!.lord); // "Rahu"
312
356
  ```
313
357
 
314
358
  ---
315
359
 
316
- ### Types
360
+ ## Types
361
+
362
+ <details>
363
+ <summary><strong>Core Types</strong> — GeoLocation, TimePeriod</summary>
317
364
 
318
365
  ```typescript
319
366
  interface GeoLocation {
@@ -326,45 +373,70 @@ interface TimePeriod {
326
373
  start: Date;
327
374
  end: Date;
328
375
  }
376
+ ```
377
+ </details>
329
378
 
330
- // ── Pancha Anga ──────────────────────────────────────────────────────────────
379
+ <details>
380
+ <summary><strong>Pancha Anga</strong> — TithiInfo, NakshatraInfo, YogaInfo, KaranaInfo, VaraInfo</summary>
331
381
 
382
+ ```typescript
332
383
  interface TithiInfo {
333
- index: number; // 029
384
+ index: number; // 0-29
334
385
  name: string; // e.g. "Shukla Pratipada"
335
- paksha: string; // "Shukla"/"Krishna" (en), "शुक्ल"/"कृष्ण" (sa/hi) — localized
336
- number: number; // 115 within the paksha
386
+ paksha: string; // "Shukla"/"Krishna" (en), "शुक्ल"/"कृष्ण" (sa/hi)
387
+ number: number; // 1-15 within the paksha
337
388
  completionPercentage: number;
338
389
  endTime: Date | null;
339
390
  }
340
391
 
341
392
  interface NakshatraInfo {
342
- index: number; // 026
393
+ index: number; // 0-26
343
394
  name: string;
344
- pada: number; // 14
395
+ pada: number; // 1-4
345
396
  degreesInNakshatra: number;
346
397
  completionPercentage: number;
347
398
  endTime: Date | null;
348
399
  }
349
400
 
350
401
  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)
402
+ startTime: Date | null; // null when isActiveAtSunrise is true
403
+ isActiveAtSunrise: boolean; // true = present at sunrise; false = started mid-day
353
404
  }
354
405
 
355
406
  // DailyNakshatraInfo, DailyYogaInfo, DailyKaranaInfo follow the same pattern
356
407
 
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).
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
+ }
359
414
 
360
- // ── Lunar calendar ───────────────────────────────────────────────────────────
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>
361
426
 
427
+ <details>
428
+ <summary><strong>Lunar Calendar</strong> — ChandraMasaInfo, SamvatInfo, MasaInfo</summary>
429
+
430
+ ```typescript
362
431
  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
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
368
440
  }
369
441
 
370
442
  interface SamvatInfo {
@@ -372,101 +444,83 @@ interface SamvatInfo {
372
444
  shakaSamvat: number; // e.g. 1946
373
445
  }
374
446
 
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
447
  interface MasaInfo {
392
- index: number; // 0 = Mesha 11 = Meena (solar month)
393
- name: string; // e.g. "Dhanu"
448
+ index: number; // 0 = Mesha ... 11 = Meena (solar month)
449
+ name: string;
394
450
  }
395
451
 
396
- interface KaranaInfo {
397
- index: number;
452
+ interface RashiInfo {
453
+ index: number; // 0 = Mesha ... 11 = Meena
398
454
  name: string;
399
- completionPercentage: number;
400
- endTime: Date | null;
401
- type: 'fixed' | 'movable';
402
455
  }
456
+ ```
457
+ </details>
403
458
 
404
- // ── Choghadiya ───────────────────────────────────────────────────────────────
459
+ <details>
460
+ <summary><strong>Time Slots</strong> — Choghadiya, Gowri Panchangam, Hora</summary>
405
461
 
462
+ ```typescript
406
463
  type ChoghadiyaQuality = 'auspicious' | 'inauspicious' | 'neutral';
407
464
 
408
465
  interface ChoghadiyaSlot extends TimePeriod {
409
466
  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", "शुभ", "शुभम्")
467
+ name: string; // e.g. "Amrit", "Kaal" (localized)
468
+ quality: ChoghadiyaQuality;
469
+ qualityName: string; // localized: "Auspicious", "शुभ", "शुभम्"
413
470
  }
414
471
 
415
472
  interface ChoghadiyaInfo {
416
- day: ChoghadiyaSlot[]; // 8 slots (sunrise sunset)
417
- night: ChoghadiyaSlot[]; // 8 slots (sunset next sunrise)
473
+ day: ChoghadiyaSlot[]; // 8 slots (sunrise -> sunset)
474
+ night: ChoghadiyaSlot[]; // 8 slots (sunset -> next sunrise)
418
475
  }
419
476
 
420
- // ── Gowri Panchangam ─────────────────────────────────────────────────────────
421
-
422
477
  interface GowriSlot extends TimePeriod {
423
- index: number; // 07 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", "शुभ", "शुभम्")
478
+ index: number; // 0-7 within the 8-name cycle
479
+ name: string; // e.g. "Amrit", "Kaal" (localized)
480
+ quality: ChoghadiyaQuality;
481
+ qualityName: string;
427
482
  }
428
483
 
429
484
  interface GowriInfo {
430
- day: GowriSlot[]; // 8 slots (sunrise sunset)
431
- night: GowriSlot[]; // 8 slots (sunset next sunrise)
485
+ day: GowriSlot[]; // 8 slots (sunrise -> sunset)
486
+ night: GowriSlot[]; // 8 slots (sunset -> next sunrise)
432
487
  }
433
488
 
434
- // ── Hora ─────────────────────────────────────────────────────────────────────
435
-
436
489
  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; // 06 index into the Chaldean sequence above
490
+ planet: string; // e.g. "Sun", "Venus", "Mercury"
491
+ planetIndex: number; // 0-6 in Chaldean order
439
492
  }
440
493
 
441
494
  interface HoraInfo {
442
- day: HoraSlot[]; // 12 slots (sunrise sunset)
443
- night: HoraSlot[]; // 12 slots (sunset next sunrise)
495
+ day: HoraSlot[]; // 12 slots (sunrise -> sunset)
496
+ night: HoraSlot[]; // 12 slots (sunset -> next sunrise)
444
497
  }
498
+ ```
499
+ </details>
445
500
 
446
- // ── Special Yogas ────────────────────────────────────────────────────────────
501
+ <details>
502
+ <summary><strong>Special Yogas & Festivals</strong></summary>
447
503
 
504
+ ```typescript
448
505
  interface SpecialYogaInfo {
449
506
  name: string; // e.g. "Guru Pushya Yoga"
450
507
  type: 'amrit_siddhi' | 'sarvartha_siddhi' | 'ravi_pushya' | 'guru_pushya';
451
508
  }
452
509
 
453
- // ── Festivals ────────────────────────────────────────────────────────────────
454
-
455
510
  interface FestivalInfo {
456
511
  name: string; // e.g. "Diwali", "Ekadashi"
457
512
  type: 'major' | 'minor' | 'ekadashi' | 'pradosha' | 'sankranti';
458
- description?: string; // For sankranti: localized rashi name (e.g. "Makara", "मकर")
513
+ description?: string; // For sankranti: localized rashi name
459
514
  }
515
+ ```
516
+ </details>
460
517
 
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) ────────────────────────────────────────────────
518
+ <details>
519
+ <summary><strong>Jyotish (Vedic Astrology)</strong> Graha positions, Vimshottari Dasha</summary>
468
520
 
469
- 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';
470
524
 
471
525
  interface GrahaPosition {
472
526
  planet: GrahaName;
@@ -483,7 +537,8 @@ interface PlanetaryPositions {
483
537
  saturn: GrahaPosition; rahu: GrahaPosition; ketu: GrahaPosition;
484
538
  }
485
539
 
486
- type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars' | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
540
+ type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars'
541
+ | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';
487
542
 
488
543
  interface AntarDasha {
489
544
  lord: DashaLord;
@@ -495,21 +550,21 @@ interface MahaDasha {
495
550
  lord: DashaLord;
496
551
  startDate: Date;
497
552
  endDate: Date;
498
- years: number; // full duration in years (proportional for the first/partial dasha)
553
+ years: number;
499
554
  antarDashas: AntarDasha[];
500
555
  }
501
556
 
502
557
  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
558
+ currentMahaDashaLord: DashaLord;
559
+ currentIndex: number;
560
+ mahaDashas: MahaDasha[]; // 9-entry sequence starting from birth
506
561
  }
507
-
508
562
  ```
563
+ </details>
509
564
 
510
565
  ---
511
566
 
512
- ## React Native / Hermes Usage
567
+ ## React Native / Hermes
513
568
 
514
569
  Works with Expo and bare React Native (Hermes engine). Pass `timezone` as a **number**
515
570
  — IANA timezone strings (`'Asia/Kolkata'`) require `Intl`, which older Hermes versions
@@ -537,21 +592,9 @@ InteractionManager.runAfterInteractions(() => {
537
592
 
538
593
  ---
539
594
 
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
595
  ## Accuracy
553
596
 
554
- 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.
555
598
 
556
599
  | Element | Accuracy |
557
600
  |---------|----------|
@@ -562,32 +605,14 @@ Validated against [DrikPanchang.com](https://www.drikpanchang.com) for 19+ date/
562
605
  | Ayanamsa | ±0.005° vs Swiss Ephemeris |
563
606
  | Choghadiya / Hora / Gowri slots | Derived from sunrise/sunset — inherits ±2 min |
564
607
 
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
- ```
577
-
578
608
  ---
579
609
 
580
- ## Compatibility
610
+ ## Performance
581
611
 
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) | ✗ |
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 |
591
616
 
592
617
  ---
593
618
 
@@ -606,21 +631,35 @@ try {
606
631
  }
607
632
  ```
608
633
 
609
- `PanchangErrorCode` values: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
634
+ Error codes: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
610
635
  `INVALID_ELEVATION`, `INVALID_TIMEZONE`, `INVALID_AYANAMSA`, `TIMEZONE_RESOLUTION_FAILED`,
611
636
  `NO_SUNRISE`, `NO_SUNSET`, `SEARCH_DIVERGED`.
612
637
 
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).
638
+ `getMoonrise` / `getMoonset` return `null` instead of throwing when no rise/set
639
+ occurs (normal for the Moon).
615
640
 
616
641
  ---
617
642
 
618
- ## Acknowledgements
643
+ ## Compatibility
619
644
 
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.
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 |
621
652
 
622
653
  ---
623
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
+
624
663
  ## License
625
664
 
626
665
  MIT
package/dist/index.cjs CHANGED
@@ -2986,7 +2986,7 @@ function computeMasa(siderealSunLon, nameResolver) {
2986
2986
  // src/core/chandramasa.ts
2987
2987
  var SYNODIC_MONTH = 29.53059;
2988
2988
  var TROPICAL_YEAR = 365.25;
2989
- function computeChandraMasa(siderealSun, siderealMoon, nameFn) {
2989
+ function computeChandraMasa(siderealSun, siderealMoon, nameFn, system = "purnimanta") {
2990
2990
  const elongation = (siderealMoon - siderealSun + 360) % 360;
2991
2991
  const daysElapsed = elongation / 360 * SYNODIC_MONTH;
2992
2992
  const sunAtPrevNewMoon = (siderealSun - daysElapsed / TROPICAL_YEAR * 360 + 36e3) % 360;
@@ -2995,12 +2995,14 @@ function computeChandraMasa(siderealSun, siderealMoon, nameFn) {
2995
2995
  const sunAtNextNewMoon = (siderealSun + daysUntilNext / TROPICAL_YEAR * 360) % 360;
2996
2996
  const solarMonthAtNext = Math.floor(sunAtNextNewMoon / 30);
2997
2997
  const isAdhika = solarMonthAtPrev === solarMonthAtNext;
2998
- const index = (solarMonthAtPrev + 1) % 12;
2999
- const name = nameFn(index, isAdhika);
2998
+ const amantaIndex = (solarMonthAtPrev + 1) % 12;
2999
+ const amantaName = nameFn(amantaIndex, isAdhika);
3000
3000
  const isKrishnaPaksha = elongation >= 180;
3001
- const purnimantaIndex = isKrishnaPaksha ? (index + 1) % 12 : index;
3001
+ const purnimantaIndex = isKrishnaPaksha ? (amantaIndex + 1) % 12 : amantaIndex;
3002
3002
  const purnimantaName = nameFn(purnimantaIndex, false);
3003
- return { index, name, isAdhika, purnimantaIndex, purnimantaName };
3003
+ const index = system === "amanta" ? amantaIndex : purnimantaIndex;
3004
+ const name = system === "amanta" ? amantaName : purnimantaName;
3005
+ return { index, name, isAdhika, system, amantaIndex, amantaName, purnimantaIndex, purnimantaName };
3004
3006
  }
3005
3007
 
3006
3008
  // src/core/samvat.ts
@@ -3852,10 +3854,12 @@ function getInstantPanchang(date, location, options) {
3852
3854
  location
3853
3855
  );
3854
3856
  const vara = computeVara(date, sunriseUtc, getTranslations(lang).varaNames);
3857
+ const masaSystem = options?.masaSystem ?? "purnimanta";
3855
3858
  const chandramasa = computeChandraMasa(
3856
3859
  siderealSun,
3857
3860
  siderealMoon,
3858
- (idx, isAdhika) => resolveChandraMasaName(idx, lang, isAdhika)
3861
+ (idx, isAdhika) => resolveChandraMasaName(idx, lang, isAdhika),
3862
+ masaSystem
3859
3863
  );
3860
3864
  const samvat = computeSamvat(date);
3861
3865
  const chandraRashi = computeChandraRashi(
@@ -3906,7 +3910,7 @@ function getInstantPanchang(date, location, options) {
3906
3910
  const festivals = computeFestivals(
3907
3911
  tithi.index,
3908
3912
  Math.floor(siderealMoon / NAKSHATRA_SPAN),
3909
- chandramasa.index,
3913
+ chandramasa.amantaIndex,
3910
3914
  chandramasa.isAdhika,
3911
3915
  vara.index,
3912
3916
  siderealSun,
@@ -3974,10 +3978,12 @@ function getDailyPanchang(date, location, options) {
3974
3978
  );
3975
3979
  const vara = computeVara(sunriseUtc, sunriseUtc, getTranslations(lang).varaNames);
3976
3980
  const masa = computeMasa(siderealSunAtSunrise, (idx) => resolveMasaName(idx, lang));
3981
+ const masaSystem = options.masaSystem ?? "purnimanta";
3977
3982
  const chandramasa = computeChandraMasa(
3978
3983
  siderealSunAtSunrise,
3979
3984
  siderealMoonAtSunrise,
3980
- (idx, isAdhika) => resolveChandraMasaName(idx, lang, isAdhika)
3985
+ (idx, isAdhika) => resolveChandraMasaName(idx, lang, isAdhika),
3986
+ masaSystem
3981
3987
  );
3982
3988
  const samvat = computeSamvat(sunriseUtc);
3983
3989
  const chandraRashi = computeChandraRashi(
@@ -4027,7 +4033,7 @@ function getDailyPanchang(date, location, options) {
4027
4033
  const festivals = computeFestivals(
4028
4034
  tithiAtSunrise.index,
4029
4035
  Math.floor(siderealMoonAtSunrise / NAKSHATRA_SPAN),
4030
- chandramasa.index,
4036
+ chandramasa.amantaIndex,
4031
4037
  chandramasa.isAdhika,
4032
4038
  vara.index,
4033
4039
  siderealSunAtSunrise,
package/dist/index.d.cts CHANGED
@@ -10,18 +10,23 @@ interface GeoLocation {
10
10
  type AyanamsaType = 'lahiri' | 'raman' | 'krishnamurti';
11
11
  type Language = 'en' | 'sa' | 'hi';
12
12
  type Precision = 'standard' | 'high';
13
+ type MasaSystem = 'purnimanta' | 'amanta';
13
14
  interface PanchangOptions {
14
15
  timezone: number | string;
15
16
  ayanamsa?: AyanamsaType;
16
17
  language?: Language;
17
18
  computeEndTimes?: boolean;
18
19
  precision?: Precision;
20
+ /** Lunar month naming system. Default: `'purnimanta'` (North Indian). */
21
+ masaSystem?: MasaSystem;
19
22
  }
20
23
  interface InstantPanchangOptions {
21
24
  ayanamsa?: AyanamsaType;
22
25
  language?: Language;
23
26
  computeEndTimes?: boolean;
24
27
  precision?: Precision;
28
+ /** Lunar month naming system. Default: `'purnimanta'` (North Indian). */
29
+ masaSystem?: MasaSystem;
25
30
  }
26
31
 
27
32
  interface TimePeriod {
@@ -71,12 +76,18 @@ interface RashiInfo {
71
76
  name: string;
72
77
  }
73
78
  interface ChandraMasaInfo {
74
- /** 0 = Chaitra … 11 = Phalguna (Amanta / South-Indian system) */
79
+ /** Month index in the active system (0 = Chaitra … 11 = Phalguna) */
75
80
  index: number;
76
- /** Translated month name (Amanta) */
81
+ /** Translated month name in the active system */
77
82
  name: string;
78
83
  /** True when two new moons fall in the same solar month (extra/leap month) */
79
84
  isAdhika: boolean;
85
+ /** Which system `index`/`name` represent */
86
+ system: 'purnimanta' | 'amanta';
87
+ /** Month index in the Amanta (South-Indian) system */
88
+ amantaIndex: number;
89
+ /** Month name in the Amanta system */
90
+ amantaName: string;
80
91
  /** Month index in the Purnimanta (North-Indian) system */
81
92
  purnimantaIndex: number;
82
93
  /** Month name in the Purnimanta system */
@@ -516,4 +527,4 @@ declare class PanchangError extends Error {
516
527
  constructor(message: string, code: PanchangErrorCode);
517
528
  }
518
529
 
519
- export { type AntarDasha, type AyanamsaType, type ChandraMasaInfo, type ChoghadiyaInfo, type ChoghadiyaQuality, type ChoghadiyaSlot, type DailyKaranaInfo, type DailyNakshatraInfo, type DailyPanchangResult, type DailyTithiInfo, type DailyYogaInfo, type DashaLord, type FestivalInfo, GRAHA_ABBR, type GeoLocation, type GowriInfo, type GowriSlot, type GrahaName, type GrahaPosition, type HoraInfo, type HoraSlot, type InstantPanchangOptions, type InstantPanchangResult, type KaranaInfo, type Language, type MahaDasha, type MasaInfo, type NakshatraInfo, PanchangError, type PanchangErrorCode, type PanchangOptions, type PlanetaryPositions, type Precision, type RashiInfo, type SamvatInfo, type SpecialYogaInfo, type TimePeriod, type TithiInfo, type VaraInfo, type VimshottariDashaResult, type YogaInfo, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computePlanetaryPositions, computeRahuKalam, computeVimshottariDasha, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
530
+ export { type AntarDasha, type AyanamsaType, type ChandraMasaInfo, type ChoghadiyaInfo, type ChoghadiyaQuality, type ChoghadiyaSlot, type DailyKaranaInfo, type DailyNakshatraInfo, type DailyPanchangResult, type DailyTithiInfo, type DailyYogaInfo, type DashaLord, type FestivalInfo, GRAHA_ABBR, type GeoLocation, type GowriInfo, type GowriSlot, type GrahaName, type GrahaPosition, type HoraInfo, type HoraSlot, type InstantPanchangOptions, type InstantPanchangResult, type KaranaInfo, type Language, type MahaDasha, type MasaInfo, type MasaSystem, type NakshatraInfo, PanchangError, type PanchangErrorCode, type PanchangOptions, type PlanetaryPositions, type Precision, type RashiInfo, type SamvatInfo, type SpecialYogaInfo, type TimePeriod, type TithiInfo, type VaraInfo, type VimshottariDashaResult, type YogaInfo, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computePlanetaryPositions, computeRahuKalam, computeVimshottariDasha, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
package/dist/index.d.ts CHANGED
@@ -10,18 +10,23 @@ interface GeoLocation {
10
10
  type AyanamsaType = 'lahiri' | 'raman' | 'krishnamurti';
11
11
  type Language = 'en' | 'sa' | 'hi';
12
12
  type Precision = 'standard' | 'high';
13
+ type MasaSystem = 'purnimanta' | 'amanta';
13
14
  interface PanchangOptions {
14
15
  timezone: number | string;
15
16
  ayanamsa?: AyanamsaType;
16
17
  language?: Language;
17
18
  computeEndTimes?: boolean;
18
19
  precision?: Precision;
20
+ /** Lunar month naming system. Default: `'purnimanta'` (North Indian). */
21
+ masaSystem?: MasaSystem;
19
22
  }
20
23
  interface InstantPanchangOptions {
21
24
  ayanamsa?: AyanamsaType;
22
25
  language?: Language;
23
26
  computeEndTimes?: boolean;
24
27
  precision?: Precision;
28
+ /** Lunar month naming system. Default: `'purnimanta'` (North Indian). */
29
+ masaSystem?: MasaSystem;
25
30
  }
26
31
 
27
32
  interface TimePeriod {
@@ -71,12 +76,18 @@ interface RashiInfo {
71
76
  name: string;
72
77
  }
73
78
  interface ChandraMasaInfo {
74
- /** 0 = Chaitra … 11 = Phalguna (Amanta / South-Indian system) */
79
+ /** Month index in the active system (0 = Chaitra … 11 = Phalguna) */
75
80
  index: number;
76
- /** Translated month name (Amanta) */
81
+ /** Translated month name in the active system */
77
82
  name: string;
78
83
  /** True when two new moons fall in the same solar month (extra/leap month) */
79
84
  isAdhika: boolean;
85
+ /** Which system `index`/`name` represent */
86
+ system: 'purnimanta' | 'amanta';
87
+ /** Month index in the Amanta (South-Indian) system */
88
+ amantaIndex: number;
89
+ /** Month name in the Amanta system */
90
+ amantaName: string;
80
91
  /** Month index in the Purnimanta (North-Indian) system */
81
92
  purnimantaIndex: number;
82
93
  /** Month name in the Purnimanta system */
@@ -516,4 +527,4 @@ declare class PanchangError extends Error {
516
527
  constructor(message: string, code: PanchangErrorCode);
517
528
  }
518
529
 
519
- export { type AntarDasha, type AyanamsaType, type ChandraMasaInfo, type ChoghadiyaInfo, type ChoghadiyaQuality, type ChoghadiyaSlot, type DailyKaranaInfo, type DailyNakshatraInfo, type DailyPanchangResult, type DailyTithiInfo, type DailyYogaInfo, type DashaLord, type FestivalInfo, GRAHA_ABBR, type GeoLocation, type GowriInfo, type GowriSlot, type GrahaName, type GrahaPosition, type HoraInfo, type HoraSlot, type InstantPanchangOptions, type InstantPanchangResult, type KaranaInfo, type Language, type MahaDasha, type MasaInfo, type NakshatraInfo, PanchangError, type PanchangErrorCode, type PanchangOptions, type PlanetaryPositions, type Precision, type RashiInfo, type SamvatInfo, type SpecialYogaInfo, type TimePeriod, type TithiInfo, type VaraInfo, type VimshottariDashaResult, type YogaInfo, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computePlanetaryPositions, computeRahuKalam, computeVimshottariDasha, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
530
+ export { type AntarDasha, type AyanamsaType, type ChandraMasaInfo, type ChoghadiyaInfo, type ChoghadiyaQuality, type ChoghadiyaSlot, type DailyKaranaInfo, type DailyNakshatraInfo, type DailyPanchangResult, type DailyTithiInfo, type DailyYogaInfo, type DashaLord, type FestivalInfo, GRAHA_ABBR, type GeoLocation, type GowriInfo, type GowriSlot, type GrahaName, type GrahaPosition, type HoraInfo, type HoraSlot, type InstantPanchangOptions, type InstantPanchangResult, type KaranaInfo, type Language, type MahaDasha, type MasaInfo, type MasaSystem, type NakshatraInfo, PanchangError, type PanchangErrorCode, type PanchangOptions, type PlanetaryPositions, type Precision, type RashiInfo, type SamvatInfo, type SpecialYogaInfo, type TimePeriod, type TithiInfo, type VaraInfo, type VimshottariDashaResult, type YogaInfo, computeAbhijitMuhurta, computeBrahmaMuhurta, computeGowriPanchangam, computeGulikaKalam, computePlanetaryPositions, computeRahuKalam, computeVimshottariDasha, computeYamaganda, computeAyanamsa as getAyanamsa, getDailyPanchang, getInstantPanchang, getMoonrise, getMoonset, getSiderealMoonLongitude, getSiderealSunLongitude, computeSunrise as getSunrise, computeSunset as getSunset };
package/dist/index.js CHANGED
@@ -2984,7 +2984,7 @@ function computeMasa(siderealSunLon, nameResolver) {
2984
2984
  // src/core/chandramasa.ts
2985
2985
  var SYNODIC_MONTH = 29.53059;
2986
2986
  var TROPICAL_YEAR = 365.25;
2987
- function computeChandraMasa(siderealSun, siderealMoon, nameFn) {
2987
+ function computeChandraMasa(siderealSun, siderealMoon, nameFn, system = "purnimanta") {
2988
2988
  const elongation = (siderealMoon - siderealSun + 360) % 360;
2989
2989
  const daysElapsed = elongation / 360 * SYNODIC_MONTH;
2990
2990
  const sunAtPrevNewMoon = (siderealSun - daysElapsed / TROPICAL_YEAR * 360 + 36e3) % 360;
@@ -2993,12 +2993,14 @@ function computeChandraMasa(siderealSun, siderealMoon, nameFn) {
2993
2993
  const sunAtNextNewMoon = (siderealSun + daysUntilNext / TROPICAL_YEAR * 360) % 360;
2994
2994
  const solarMonthAtNext = Math.floor(sunAtNextNewMoon / 30);
2995
2995
  const isAdhika = solarMonthAtPrev === solarMonthAtNext;
2996
- const index = (solarMonthAtPrev + 1) % 12;
2997
- const name = nameFn(index, isAdhika);
2996
+ const amantaIndex = (solarMonthAtPrev + 1) % 12;
2997
+ const amantaName = nameFn(amantaIndex, isAdhika);
2998
2998
  const isKrishnaPaksha = elongation >= 180;
2999
- const purnimantaIndex = isKrishnaPaksha ? (index + 1) % 12 : index;
2999
+ const purnimantaIndex = isKrishnaPaksha ? (amantaIndex + 1) % 12 : amantaIndex;
3000
3000
  const purnimantaName = nameFn(purnimantaIndex, false);
3001
- return { index, name, isAdhika, purnimantaIndex, purnimantaName };
3001
+ const index = system === "amanta" ? amantaIndex : purnimantaIndex;
3002
+ const name = system === "amanta" ? amantaName : purnimantaName;
3003
+ return { index, name, isAdhika, system, amantaIndex, amantaName, purnimantaIndex, purnimantaName };
3002
3004
  }
3003
3005
 
3004
3006
  // src/core/samvat.ts
@@ -3850,10 +3852,12 @@ function getInstantPanchang(date, location, options) {
3850
3852
  location
3851
3853
  );
3852
3854
  const vara = computeVara(date, sunriseUtc, getTranslations(lang).varaNames);
3855
+ const masaSystem = options?.masaSystem ?? "purnimanta";
3853
3856
  const chandramasa = computeChandraMasa(
3854
3857
  siderealSun,
3855
3858
  siderealMoon,
3856
- (idx, isAdhika) => resolveChandraMasaName(idx, lang, isAdhika)
3859
+ (idx, isAdhika) => resolveChandraMasaName(idx, lang, isAdhika),
3860
+ masaSystem
3857
3861
  );
3858
3862
  const samvat = computeSamvat(date);
3859
3863
  const chandraRashi = computeChandraRashi(
@@ -3904,7 +3908,7 @@ function getInstantPanchang(date, location, options) {
3904
3908
  const festivals = computeFestivals(
3905
3909
  tithi.index,
3906
3910
  Math.floor(siderealMoon / NAKSHATRA_SPAN),
3907
- chandramasa.index,
3911
+ chandramasa.amantaIndex,
3908
3912
  chandramasa.isAdhika,
3909
3913
  vara.index,
3910
3914
  siderealSun,
@@ -3972,10 +3976,12 @@ function getDailyPanchang(date, location, options) {
3972
3976
  );
3973
3977
  const vara = computeVara(sunriseUtc, sunriseUtc, getTranslations(lang).varaNames);
3974
3978
  const masa = computeMasa(siderealSunAtSunrise, (idx) => resolveMasaName(idx, lang));
3979
+ const masaSystem = options.masaSystem ?? "purnimanta";
3975
3980
  const chandramasa = computeChandraMasa(
3976
3981
  siderealSunAtSunrise,
3977
3982
  siderealMoonAtSunrise,
3978
- (idx, isAdhika) => resolveChandraMasaName(idx, lang, isAdhika)
3983
+ (idx, isAdhika) => resolveChandraMasaName(idx, lang, isAdhika),
3984
+ masaSystem
3979
3985
  );
3980
3986
  const samvat = computeSamvat(sunriseUtc);
3981
3987
  const chandraRashi = computeChandraRashi(
@@ -4025,7 +4031,7 @@ function getDailyPanchang(date, location, options) {
4025
4031
  const festivals = computeFestivals(
4026
4032
  tithiAtSunrise.index,
4027
4033
  Math.floor(siderealMoonAtSunrise / NAKSHATRA_SPAN),
4028
- chandramasa.index,
4034
+ chandramasa.amantaIndex,
4029
4035
  chandramasa.isAdhika,
4030
4036
  vara.index,
4031
4037
  siderealSunAtSunrise,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panchang-ts",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Pure TypeScript Hindu Panchang calculations. Tithi, Nakshatra, Yoga, Karana, Vara, and more. Offline-first, React Native compatible.",
5
5
  "author": "Ishank",
6
6
  "license": "MIT",