panchang-ts 5.2.1 → 5.4.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
@@ -6,10 +6,10 @@
6
6
  Pure TypeScript Hindu Panchang (almanac), Jyotish, and Birth Chart calculations.
7
7
  Zero runtime dependencies. Works offline in React Native (Hermes), Node.js, and browsers.
8
8
 
9
- **Fast** (~0.25 ms trimmed, ~0.41 ms full) · **Typed** (full TypeScript) · **Offline** (pure JS math) · **8,805 tests across 138 files**
9
+ **Fast** (~0.16 ms trimmed, ~0.30 ms full) · **Typed** (full TypeScript) · **Offline** (pure JS math) · **9,207 tests across 146 files**
10
10
 
11
11
  > 📖 **Full documentation: [dharmagya.app/docs/panchang-ts](https://dharmagya.app/docs/panchang-ts)**
12
- > This README covers install, quick start, and the 5.1 → 5.2, 5.0 → 5.1 and 4.x → 5 migrations
12
+ > This README covers install, quick start, and the 5.3 → 5.4, 5.1 → 5.2, 5.0 → 5.1 and 4.x → 5 migrations
13
13
  > in full, plus a per-feature quick reference. The complete reference (every option, result field,
14
14
  > table format, accuracy bound and performance note) lives on the docs site.
15
15
 
@@ -38,8 +38,8 @@ const result = getDailyPanchang(
38
38
  { latitude: 23.1765, longitude: 75.7885 }, // Ujjain, India
39
39
  { timezone: 330 }, // IST = UTC+5:30 = 330 minutes
40
40
  );
41
- // → DailyPanchangResult | null. Null only at polar latitudes where
42
- // sunrise can't be computed. Anywhere else, narrow with `if (!result) return;`.
41
+ // → DailyPanchangResult | null. Null only when no sunrise-to-sunrise day starts on that date
42
+ // (polar latitudes, or an offset far from local solar time). Narrow with `if (!result) return;`.
43
43
 
44
44
  console.log(result!.angas.tithis[0].name); // "Krishna Chaturdashi"
45
45
  console.log(result!.angas.nakshatras[0].name); // "Mrigashira"
@@ -185,6 +185,230 @@ pre-computed for Varanasi, which is a fourth point again.
185
185
 
186
186
  ---
187
187
 
188
+ ## Upgrading to 5.4
189
+
190
+ Correctness fixes from a full TypeScript and Go audit, festival rules brought in line with the
191
+ reference almanac and its competitors, a corrected Yoni koota table, one new export and two new
192
+ festival keys. Nothing is removed or renamed and no signature changes, but many published values
193
+ move, so read the first sections before upgrading if you cache or diff output. The Go module
194
+ (`source/go/v5`) carries the same changes byte for byte.
195
+
196
+ ### Festival days now follow the reference almanac
197
+
198
+ Every rule below was checked against published dates: the reference almanac first, then AstroSage
199
+ and other almanacs, with the classical rule (Nirnaya Sindhu, Dharma Sindhu) as the tie-breaker.
200
+ The dates relied on are pinned in `testdata/almanac/almanac-festival-selection-2026-09.json` and
201
+ `almanac-festival-rules-2026-09-nakshatra-solar.json`. Over 5 cities and 1900 to 2100 every
202
+ once-a-year festival now fires exactly once per year and every monthly one at most once per paksha
203
+ or month; before, most of them were listed on two consecutive days in some years. Two exceptions
204
+ remain while their rules are deferred (below): Phagli still takes every day whose sunrise holds
205
+ Purnima (two days in 2033, none in 2018 at Delhi), and a paksha whose Trayodashi touches no
206
+ pradosha window has no Pradosh.
207
+
208
+ - **A tithi current at two sunrises** (vriddhi): the first day, as the reference publishes (Anant
209
+ Chaturdashi 2018-09-23, Ghatasthapana 2015-10-13), except the Tritiya vratas (Hariyali, Kajari
210
+ and Hartalika Teej, Gangaur) and Jagannath Rath Yatra, which take the second (Hartalika Teej
211
+ 2006-08-27). Ugadi, Gudi Padwa, Hanuman Jayanti, Navaratri, Durga Ashtami, Guru, Sharad, Kartika
212
+ and Vat Savitri Purnima, Mahalaya Amavasya and others lose their second-day duplicate.
213
+ - **Festivals chosen by a kala** use the reference's windows (madhyahna the third fifth of
214
+ daytime, aparahna the fourth, pradosha sunset to sunset + night/5, nishita the 8th night
215
+ muhurta). The day whose window the tithi covers most decides Ganesh and Vinayaka Chaturthi
216
+ (madhyahna), Dhanteras, Diwali and Parashurama Jayanti (pradosha) and Govardhan Puja (daytime),
217
+ the earlier day winning a tie or when both days' windows are full. Masik and Maha Shivaratri follow the reference's nishita ladder, and Dussehra its aparahna ladder
218
+ with the Shravana rule. Rama Navami (madhyahna), Bhai Dooj and Vat Savitri Amavasya (aparahna)
219
+ take the last day whose window the tithi touches, Narak Chaturdashi the first day whose
220
+ arunodaya it touches, and Vasant Panchami the first day on which Panchami has begun by two
221
+ fifths of the daytime. Vinayaka Chaturthi in December 2025 at Delhi drops its 12-23 duplicate
222
+ and keeps 12-24.
223
+ - **Raksha Bandhan, Yajur Upakarma, Nag Panchami and Akshaya Tritiya** take the first sunrise day
224
+ on which the tithi lasts at least 3 muhurtas, else the day it begins. Akshaya Tritiya 2020 at
225
+ Delhi drops its 04-25 duplicate and keeps 04-26.
226
+ - **Pradosh vrat** is the day whose pradosha window (sunset to sunset + night/5) overlaps
227
+ Trayodashi most; it reproduces every reference date for Delhi 2025 and 2027 (Ravi Pradosh
228
+ 2025-02-09 was missing).
229
+ - **Holika Dahan** is a new festival key, `holika_dahan`, on the evening the reference's Bhadra
230
+ ladder chooses; **`holi` is now the next day** (Rangwali Holi), as the reference publishes it.
231
+ That holds for `getDailyPanchang`, the listings and the tables. `getInstantPanchang` reports by
232
+ the tithi at the instant, so an instant in Phalguna Purnima lists `holika_dahan` and `holi`
233
+ together, and it never lists `karthigai_deepam` (it reports `masik_karthigai` on that day).
234
+ - **Chhath** is anchored on the Shashthi sunrise day with Nahay Khay, Kharna and Usha Arghya at
235
+ -2, -1 and +1 days; **Maha Navami** takes the first day whose sunset less two muhurtas follows the
236
+ start of Navami (2015-10-21, 2027-10-08); **Saddula Bathukamma** moves to Durgashtami;
237
+ **Varamahalakshmi** is the Friday in the 7 days ending on the Shravana Purnima sunrise day.
238
+ - **Masik Karthigai** is one day per Krittika transit (the first day Krittika holds at sunset, else
239
+ at sunrise); it fired on two consecutive days in about three months of four. **Karthigai Deepam**
240
+ is a new festival key, `karthigai_deepam`, on the Karthigai-month Krittika day nearest the full
241
+ moon, for regions `all` and `tamil-nadu`. On that day those two regions list it in place of
242
+ `masik_karthigai`; every other region still lists `masik_karthigai`, so a table built for `all`
243
+ and filtered by region afterwards loses that month's Masik Karthigai outside Tamil Nadu.
244
+ - **Onam** is exactly one day a year: the Thiruvonam of Chingam, the first of two Chingam sunrises,
245
+ the day holding it when it holds no sunrise, and the later of two Thiruvonams in one Chingam
246
+ (at Kochi, 2024-09-15 and 2013-09-16).
247
+ - **Shravan Somvar, Mangala Gauri, Kartik Somvar and Magha Shanivar** follow `masaSystem`: the
248
+ default purnimanta gives the North Indian Mondays (Sawan Somwar 2025: 07-14, 07-21, 07-28, 08-04),
249
+ `amanta` the southern ones as before, region `nepal` the solar month. This changes the default
250
+ output: a caller that never sets `masaSystem` now gets the North Indian dates.
251
+ - Deferred for lack of a reference capture: a Pradosh fallback when Trayodashi touches no pradosha
252
+ window, Phagli, and the Guru Purnima short-udaya refinement.
253
+
254
+ ### Yoni koota and Pathu Porutham Yoni
255
+
256
+ **Yoni koota (`computeAshtakoot`)** now uses the published five-level table (4 same animal, 3
257
+ friendly, 2 neutral, 1 unfriendly, 0 enemy); the old one never awarded 3 and matched no published
258
+ chakra. The table is the chakra of Mahidhar Sharma's tika on Muhurta Chintamani, as carried by
259
+ Frawley and the Jagannatha Hora port PyJHora; the sources are in
260
+ `testdata/charts/yoni-koota-references.json`. 550 of the 1,296 valid pairs of natal Moons change
261
+ total (372 by -1, 166 by +1, 12 by +2). **Pathu Porutham Yoni** (`computePathuPorutham`) is now the
262
+ Tamil pass/fail test, failing only on the seven enemy pairs, and no longer reads the Ashtakoot
263
+ score; its description reads, for example, `"horse ↔ snake, not enemies"`.
264
+
265
+ ### Day-valued results west of UTC, and the Odisha new year
266
+
267
+ `computeSankrantisForYear` (`SankrantiEvent.date`) and the solar branch of `getHinduNewYear`
268
+ (Tamil Nadu, Kerala, Punjab, West Bengal, Assam) now return the UTC midnight that falls within the
269
+ local day, like every other day-valued result, so formatting them in the location's zone gives the
270
+ right day (Puthandu 2025 in New York read as April 12). At UTC and east of it, IST included, every
271
+ value is byte-identical. **`getHinduNewYear(year, 'odisha')`** returns Pana Sankranti, the Mesha
272
+ Sankranti day, instead of Chaitra Shukla Pratipada: the transit's civil date, or, when the transit
273
+ falls later than 0.315 of the way through the night after sunset, the date of the sunrise that ends
274
+ that night. That cutoff is fitted to the reference
275
+ almanac's Bhubaneswar dates (2024-04-13 for a 21:15 transit, 2028-04-14 for 21:47); no textbook
276
+ Odia rule reproduces them. The cutoff is a fraction of the local night, so the date depends on the
277
+ location (2028 is 04-13 at Delhi and 04-14 at Bhubaneswar), and it can fall a day before
278
+ `SankrantiEvent.date`, which moves a transit after sunset to the next day (Bhubaneswar 2024: 04-13
279
+ and 04-14). The festival listings are not part of this change: `computeFestivalsForYear` returns
280
+ the local midnight of each day and `computeFestivalsInRange` the range start's local time of day,
281
+ and both read correctly in the location's zone.
282
+
283
+ ### Values that move
284
+
285
+ - **Brahma Muhurta** (`muhurtas.brahma`, `computeBrahmaMuhurta`) is now the 14th of the 15 night
286
+ muhurtas, from two night muhurtas to one before sunrise (about 96 to 48 minutes at an
287
+ equinox). It used daylight/30, a 24 minute window ending 24 minutes before sunrise. The daily
288
+ panchang measures the night from sunset to the next sunrise, so the window's midpoint is
289
+ exactly Pratah Sandhya's start, which the reference almanac captures in `testdata` confirm;
290
+ they hold no Brahma Muhurta value of their own.
291
+ - **Year and range listings in zones with daylight saving** (`computeFestivalsForYear`,
292
+ `computeFestivalsInRange`, `computeAuspiciousDatesForYear`, `computeAuspiciousDatesInRange`,
293
+ `computeMoonPhasesForYear`, `computeEclipsesForYear`) now step civil days in the zone. They
294
+ used a fixed 24 hour step from an offset taken on 1 July, so in America and Europe they skipped
295
+ the spring-forward day, listed the fall-back day twice, included the previous 31 December and
296
+ dropped the requested one, and stamped winter dates at 23:00 of the previous day (Diwali 2026
297
+ in New York came back as `2026-11-09T04:00Z`, 23:00 on 8 November in the zone and 9 November at
298
+ the July offset; it is now `2026-11-08T05:00Z`, local midnight). Numeric offsets and zones without
299
+ daylight saving, IST included, are byte-identical.
300
+ - **Festivals that went missing**: Sankashti Chaturthi when Chaturthi touches no moonrise (about
301
+ 7% of Krishna pakshas, for example Delhi 2025-10-10), Ugadi, Gudi Padwa and Navaratri when a
302
+ kshaya Pratipada follows an adhika month, the Smarta Ekadashi of a vriddha Ekadashi followed by
303
+ a kshaya Dwadashi, and Onam in adhika Bhadrapada years (2012, 2096). Across 9 cities over
304
+ 1900 to 2100 these four fixes add 1,677 entries and remove or move none (the rule changes above
305
+ restore further dates of their own, and do move and remove entries).
306
+ - **`getHinduNewYear`** on a kshaya Chaitra Shukla Pratipada now returns the containing day, the
307
+ day the library's own Ugadi falls on and the reference publishes (2026-03-19, was 2026-03-20).
308
+ - **`convertHinduToGregorian`** now finds Adhika Chaitra Krishna-paksha dates in purnimanta mode.
309
+ - **`computeEclipsesInRange` / `computeEclipsesForYear`** select by the eclipse's peak, not its
310
+ syzygy, so an eclipse near a range or year boundary is listed exactly once.
311
+ - **`computeEkadashiDatesForYear`** covers the local calendar year exactly: a fast on local
312
+ 31 December moves from the next year's list into its own (Pune 1912 loses 1912-01-01, which is
313
+ the 1911-12-31 fast). The values themselves are unchanged: each is still the UTC midnight that
314
+ falls within the local day of the fast, so format it in the location's zone.
315
+ - **Years 1900 and 2100** now work at every offset in the year listings and table builders. The
316
+ festival, moon-phase, eclipse and auspicious-date listings, the lunar `getHinduNewYear`,
317
+ `convertHinduToGregorian` and the table builders reject years 0 to 99 with `INVALID_DATE` instead
318
+ of silently reading them as 1900 to 1999. `computeEkadashiDatesForYear` and
319
+ `computeSankrantisForYear` still take any year, so 99 now means the year 99 rather than 1999,
320
+ and the solar `getHinduNewYear` returns `null` for such a year (5.3 returned the 1999 date).
321
+ - **Daily panchang edges**: a civil day with no sunrise at a polar transition returns `null`
322
+ instead of the next day's panchang, and `getInstantPanchang` returns `null` for an instant whose
323
+ Hindu day has no sunrise to start it; `moon.set` is no longer taken from the next civil day on a
324
+ day with no moonrise; one eclipse no longer shows on two consecutive days when sunrise falls
325
+ between syzygy and peak; a solar eclipse whose peak is below the horizon is described by its
326
+ deepest visible phase (so its description says it is visible while `visibleFromLocation`, which
327
+ is about the peak, stays `false`); `computeEndTimes: false` no longer changes `specialYogas`; the
328
+ Bhadra boundary is no longer about a minute off.
329
+ - **Dashas**: Ashtottari and Yogini antardashas of the birth mahadasha are now those of the full
330
+ mahadasha clipped at birth, as Vimshottari's already were; Narayan `{ duration: 'variable' }`
331
+ gives 12 years to a sign whose lord occupies it (it gave 0, or 1 for an exalted lord); sub-periods
332
+ now end exactly at their parent's end (they drifted by up to 8 ms).
333
+ - **Shadbala**: Nathonatha Bala is continuous across sunrise and sunset (it jumped by 60 virupas);
334
+ cached Shadbala and Bhava Bala totals move by up to about 34 virupas in the pinned charts, and
335
+ more for a birth just after sunrise in a long night; the Varshaphala year lord can change with
336
+ them.
337
+ - **Sade Sati** no longer skips a retrograde re-entry when finding `nextArcStart`, which could be
338
+ about 21 years late.
339
+ - **Kemadruma** no longer counts the Sun, consistent with Sunapha and Anapha.
340
+ - **`computeVarjyam`** returns `null` when the index is not the nakshatra in force at sunrise,
341
+ instead of a meaningless window (usually a few seconds, occasionally a plausible-looking hour).
342
+ - **Placidus-KP houses** between about 66.2 and 66.56 degrees of latitude now return cusps instead
343
+ of `PLACIDUS_DIVERGED`.
344
+
345
+ ### New export
346
+
347
+ - `computeVimshottariPratyantarIn(mahaDasha, antardasha)`: the pratyantars of an antardasha split
348
+ over its full length and clipped, which is what the birth antardasha needs.
349
+ `computeVimshottariPratyantar` is unchanged.
350
+
351
+ ### Input handling
352
+
353
+ Only inputs that used to hang, crash with a raw `TypeError`, or return garbage:
354
+
355
+ - An Invalid Date, or an instant beyond the rise and set solver's range, throws `INVALID_DATE`
356
+ instead of hanging `getSunrise` and friends forever.
357
+ - An unknown house system, divisional, graha, yoga type or node-aspect mode, or a vara outside 0
358
+ to 6, throws `PanchangError` `INVALID_INPUT`. Before, these crashed with a `TypeError`, returned
359
+ an Invalid Date window, or (yoga type, node-aspect mode) were silently ignored.
360
+ `computeVimshottariPratyantar` with an unknown lord throws `INVALID_INPUT` too (a plain `Error`
361
+ before), and the sidereal-longitude helpers throw `INVALID_DATE` for an Invalid Date (5.3
362
+ returned `NaN`).
363
+ - An Invalid Date in `getUpcomingSolarEclipse` or `getUpcomingLunarEclipse` (5.3: `null`) or in
364
+ `computePlanetaryPositions` (5.3: `NaN` longitudes) throws `INVALID_DATE`. A non-integer or
365
+ out-of-range `timezoneOffsetMinutes` in `buildEclipsesTable` or `buildMoonPhasesTable` throws
366
+ `INVALID_TIMEZONE`; 5.3 built the table.
367
+ - An unknown table language throws a `RangeError` in TypeScript (`INVALID_INPUT` in Go); 5.3
368
+ built the festivals table anyway and crashed with a `TypeError` in the other two builders.
369
+ - One relaxation: `janmaRashi: null` or `janmaNakshatra: null` in `getDailyPanchang` now means the
370
+ same as leaving it out (5.3 threw a `RangeError`).
371
+ - A Moon longitude outside [0, 360) is wrapped into it by all three moon-longitude dashas; NaN or
372
+ Infinity throws `INVALID_INPUT`.
373
+ - `formatInZone` renders any integer offset correctly and throws `INVALID_TIMEZONE` for a
374
+ fractional one (5.3 printed `+00:undefined`).
375
+ - `computePrashnaChart` and `computeKpCuspalSubLords` keep their KP defaults when an option is
376
+ passed as `undefined`.
377
+
378
+ **Documented, not changed**: an omitted `timezone` falls back to the host's zone in untyped
379
+ JavaScript; the Go port reports `INVALID_TIMEZONE` instead, and accepts the zone names `""` (UTC)
380
+ and `"Local"` (the host), which TypeScript rejects.
381
+
382
+ ### Faster, with no output changes
383
+
384
+ The full parity documents, every table file and every golden are byte-identical before and
385
+ after. Measured on the same machine, alternating builds, medians:
386
+
387
+ | call | TypeScript | Go |
388
+ |---|---|---|
389
+ | `getDailyPanchang`, a year of days | 1.4x | 1.3x |
390
+ | `getDailyPanchang`, a year in `America/New_York` | 1.5x | 1.4x |
391
+ | `getInstantPanchang`, a year of instants | 1.5x | 1.4x |
392
+ | `computeFestivalsForYear` | 1.8x | 1.5x |
393
+ | `buildMuhurtaTable`, one year | 2.2x | 2.0x |
394
+ | `convertHinduToGregorian` | 6.5x | 5.0x |
395
+ | `getUpcomingEclipses` | 4.6x | 3.9x |
396
+ | `computeSadeSati` | 18x | 16x |
397
+ | `computeKpCuspalSubLords` / `computeBhava` | 12x | 38x |
398
+ | a birth-chart bundle | 2.2x | 2.5x |
399
+
400
+ Most of the causes were repeated work: the same new-moon searches and planet positions recomputed
401
+ across days and charts (now kept in bounded caches keyed by every input, so no result depends on
402
+ which calls ran before it), house cusps computing nine planet positions they never read, the
403
+ converters and table builders building whole days to read four labels, Sade Sati sampling Saturn at
404
+ steps where it cannot change sign, and a timezone formatter rebuilt on every call. One was not: in
405
+ TypeScript, the sine and cosine behind every series evaluation passed their reduced argument through
406
+ module-level variables, and now keep it in locals with the same operations in the same order. That
407
+ change alone gives the chart primitives their whole speedup and most of the gain on a distinct day
408
+ and in the Ekadashi and Sankranti listings; the caches give nearly all of it on a repeated day.
409
+
410
+ ---
411
+
188
412
  ## Upgrading to 5.2
189
413
 
190
414
  No type breaks and no removed exports. What changes is output: a set of dates that
@@ -773,7 +997,7 @@ console.log(i.angas.tithi.name, i.angas.nakshatra.name);
773
997
  📖 [Festivals →](https://dharmagya.app/docs/panchang-ts/festivals)
774
998
 
775
999
  ```typescript
776
- r.festivals.forEach(f => console.log(f.key, f.name, f.type, f.deferralDate));
1000
+ r.festivals.forEach(f => console.log(f.key, f.name, f.type));
777
1001
  // `name` is localized, so match on `key`, never on `name`:
778
1002
  const hasDiwali = r.festivals.some(f => f.key === 'diwali');
779
1003
 
@@ -914,7 +1138,7 @@ import {
914
1138
  } from 'panchang-ts';
915
1139
 
916
1140
  const v = computeVarshaphala(birth, 30, loc); // Tajik + Muntha + 27 Sahams
917
- computeTithiPravesha(birth, 30, loc); // preserves natal tithi exactly
1141
+ computeTithiPravesha(birth, 30, loc); // natal tithi's Sun-Moon separation returns
918
1142
  computeKpSubLord(45.5).subLord; // 243 sub-divisions
919
1143
  computePrashnaChart(questionTime, querentLoc); // horary, Placidus-KP default
920
1144
  ```
@@ -995,7 +1219,7 @@ InteractionManager.runAfterInteractions(() => {
995
1219
 
996
1220
  📖 [Full accuracy notes →](https://dharmagya.app/docs/panchang-ts/accuracy)
997
1221
 
998
- 8,805 tests across 138 files, including fixtures cross-verified against reference
1222
+ 9,207 tests across 146 files, including fixtures cross-verified against reference
999
1223
  panchang calculations spanning 2025-2026 across 10 Indian cities plus New York,
1000
1224
  London, Sydney, Dubai, Singapore (diaspora fixtures cover DST on
1001
1225
  `America/New_York`).
@@ -1015,10 +1239,14 @@ London, Sydney, Dubai, Singapore (diaspora fixtures cover DST on
1015
1239
  | Varjyam windows | count + position vs the reference almanac over a 61-day / two-nakshatra-cycle sweep, ≤2 min (62/62 windows) |
1016
1240
  | Sade Sati arc start/end | ±1-2 days vs authoritative ephemerides |
1017
1241
 
1018
- **Festival dating** uses tithi-at-sunrise; a few festivals have authorities on
1019
- other rules (tithi-at-midnight for Janmashtami / Shivaratri / Diwali,
1020
- madhyahna-vyapini for Ganesh Chaturthi edge years) where output can drift
1021
- ±1 day. The exact list is
1242
+ **Festival dating** follows the reference almanac's day choice when a tithi
1243
+ touches two days: the first of two sunrise days (the later one for the Teej and
1244
+ Gangaur Tritiya vratas and Rath Yatra); the day holding more of madhyahna (Ganesh
1245
+ and Vinayaka Chaturthi), of pradosha (Diwali, Dhanteras, Pradosh vrat) or of the
1246
+ nishita muhurta (Shivaratri); three muhurtas after sunrise for Raksha Bandhan,
1247
+ Upakarma, Nag Panchami and Akshaya Tritiya; the Holika Dahan ladder, with Holi on
1248
+ the next day; Chhath counted from its Shashthi. Rules with few published years to
1249
+ check against can still drift ±1 day. The exact list is
1022
1250
  [documented](https://dharmagya.app/docs/panchang-ts/accuracy#festival-tradeoff).
1023
1251
 
1024
1252
  **Detection conventions:** Aadal / Vidaal follow the classical Moon-from-Sun
@@ -1032,36 +1260,40 @@ tyajya spells). Do Ghati does not rotate by weekday.
1032
1260
 
1033
1261
  📖 [Full performance notes →](https://dharmagya.app/docs/panchang-ts/performance)
1034
1262
 
1035
- Measured at Pune, Apple M-series, Node (median of 11 processes). **Distinct
1036
- days** is the calendar-scan cost; **same day repeated** is what a UI
1037
- re-rendering one date sees. Last column is published 4.3.1, benchmarked beside
1038
- this release.
1263
+ Measured at Pune, Apple M-series, Node (median of 11 processes, the three builds
1264
+ interleaved). **Distinct days** is the calendar-scan cost; **same day repeated** is
1265
+ what a UI re-rendering one date sees. The last two columns are the published 5.3.0
1266
+ and 4.3.1 packages, benchmarked beside this release.
1039
1267
 
1040
- | `getDailyPanchang` call | Distinct days | Same day repeated | 4.3.1 (distinct) |
1041
- |---|---|---|---|
1042
- | Default (all sections + end-times) | **~0.41 ms** | **~0.17 ms** | ~6.06 ms |
1043
- | `sections: []` + `computeEndTimes: false` | ~0.25 ms | ~0.14 ms | n/a |
1044
- | `getInstantPanchang` | ~0.21 ms | ~0.10 ms | ~0.43 ms |
1268
+ | `getDailyPanchang` call | Distinct days | Same day repeated | 5.3.0 (distinct) | 4.3.1 (distinct) |
1269
+ |---|---|---|---|---|
1270
+ | Default (all sections + end-times) | **~0.30 ms** | **~0.10 ms** | ~0.41 ms | ~5.83 ms |
1271
+ | `sections: []` + `computeEndTimes: false` | ~0.16 ms | ~0.05 ms | ~0.26 ms | n/a |
1272
+ | `getInstantPanchang` | ~0.13 ms | ~0.02 ms | ~0.22 ms | ~0.44 ms |
1045
1273
 
1046
- **A default day is ~15× cheaper than 4.3.1**, a repeated day ~37×. The levers:
1274
+ **A default day is ~1.4× cheaper than 5.3.0 and ~20× cheaper than 4.3.1**, a
1275
+ repeated day ~1.9× and ~66×. The levers:
1047
1276
 
1048
1277
  - **`sections`** skips the optional ephemeris-backed blocks (`'festivals'`,
1049
- `'eclipse'`, `'moonTimes'`, `'lunarWindows'`). Narrowing is exactly
1050
- output-neutral: every field a narrowed call computes is identical to the full
1051
- call's; omitted sections sit at their documented `null` / `[]`.
1052
- - **`computeEndTimes: false`** drops the `endTime` transition searches;
1053
- ~5-10%. Use it to drop fields you don't want, not to go faster.
1054
-
1055
- Range helpers narrow internally: `computeEkadashiDatesForYear` **~18 ms/year**
1056
- (4.3.1: ~2,360), `computeFestivalsInRange` **~131 ms/year** (~2,180),
1057
- `computeSankrantisForYear` **~3.3 ms/year** (~154). Solar rise/set events are
1278
+ `'eclipse'`, `'moonTimes'`, `'lunarWindows'`). A narrowed call's values equal
1279
+ the full call's and omitted sections sit at their documented `null` / `[]`,
1280
+ with three couplings: `'festivals'` also fills `moon.rise` and
1281
+ `inauspicious.bhadra`, which it needs, and the Surya/Chandra Grahan entry in
1282
+ `festivals` comes with `'eclipse'`, so `['festivals']` alone omits it.
1283
+ - **`computeEndTimes: false`** drops the published end times and the yoga and
1284
+ karana transition searches (`specialYogas` still covers the whole day);
1285
+ ~2-5%. Use it to drop fields you don't want, not to go faster.
1286
+
1287
+ Range helpers narrow internally: `computeEkadashiDatesForYear` **~16 ms/year**
1288
+ (5.3.0: ~19, 4.3.1: ~2,410), `computeFestivalsInRange` **~79 ms/year** (~136, ~2,230),
1289
+ `computeSankrantisForYear` **~2.8 ms/year** (~3.3, ~160). Solar rise/set events are
1058
1290
  cached process-wide (bounded, 20k entries), which also makes them
1059
1291
  single-valued.
1060
1292
 
1061
1293
  One deliberate regression: raw chart primitives (`computeRashiChart`,
1062
- `computeNavamsa`) cost ~0.32 ms vs ~0.10 in 4.3.1, the price of an
1294
+ `computeNavamsa`) cost ~0.18 ms vs ~0.10 in 4.3.1 (5.3.0: ~0.32), the price of an
1063
1295
  order-of-magnitude accuracy gain against JPL DE441. `computeShadbala` /
1064
- `computeBhavaBala` went the other way, ~2× faster.
1296
+ `computeBhavaBala` went the other way, ~3.6× to 3.7× faster than 4.3.1.
1065
1297
 
1066
1298
  ---
1067
1299
 
@@ -1085,11 +1317,15 @@ try {
1085
1317
  Error codes: `INVALID_DATE`, `INVALID_LATITUDE`, `INVALID_LONGITUDE`,
1086
1318
  `INVALID_ELEVATION`, `INVALID_TIMEZONE`, `INVALID_AYANAMSA`, `INVALID_INPUT`,
1087
1319
  `TIMEZONE_RESOLUTION_FAILED`, `NO_SUNRISE`, `NO_SUNSET`, `SEARCH_DIVERGED`,
1088
- `CIRCUMPOLAR` (Placidus-KP houses above ±66.5°).
1320
+ `CIRCUMPOLAR` and `PLACIDUS_DIVERGED` (Placidus-KP houses beyond the polar
1321
+ circles, |latitude| above about 66.56°; whole-sign and equal houses still work
1322
+ there), `SAHAM_DEPENDENCY_ERROR` (an internal Varshaphala saham ordering fault).
1089
1323
 
1090
1324
  **Polar locations.** `getDailyPanchang` and `getInstantPanchang` return `null`
1091
1325
  rather than throwing: the Hindu day is undefined when sunrise can't be
1092
- computed. The low-level `getSunrise` / `getSunset` primitives still throw
1326
+ computed, including on the civil day a polar night or midnight sun ends,
1327
+ whose first sunrise belongs to the next day. The low-level `getSunrise` /
1328
+ `getSunset` primitives still throw
1093
1329
  `PanchangError(NO_SUNRISE)` / `PanchangError(NO_SUNSET)` for direct callers
1094
1330
  who need the precise reason. `getMoonrise` / `getMoonset` return `null` (normal
1095
1331
  for the Moon).