panchanga 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +272 -0
- package/dist/ayanamsha.d.ts +84 -0
- package/dist/ayanamsha.d.ts.map +1 -0
- package/dist/ayanamsha.js +124 -0
- package/dist/ayanamsha.js.map +1 -0
- package/dist/eclipses.d.ts +58 -0
- package/dist/eclipses.d.ts.map +1 -0
- package/dist/eclipses.js +132 -0
- package/dist/eclipses.js.map +1 -0
- package/dist/elements.d.ts +230 -0
- package/dist/elements.d.ts.map +1 -0
- package/dist/elements.js +603 -0
- package/dist/elements.js.map +1 -0
- package/dist/festivals.d.ts +145 -0
- package/dist/festivals.d.ts.map +1 -0
- package/dist/festivals.js +927 -0
- package/dist/festivals.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/panchanga.d.ts +78 -0
- package/dist/panchanga.d.ts.map +1 -0
- package/dist/panchanga.js +76 -0
- package/dist/panchanga.js.map +1 -0
- package/dist/rules.d.ts +161 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +1058 -0
- package/dist/rules.js.map +1 -0
- package/dist/time.d.ts +306 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +637 -0
- package/dist/time.js.map +1 -0
- package/dist/types.d.ts +169 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/package.json +57 -0
package/dist/rules.js
ADDED
|
@@ -0,0 +1,1058 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/rules.ts — per-festival rule DATA.
|
|
3
|
+
*
|
|
4
|
+
* This module encodes the HSNA v1 festival rule set as `FestivalRule[]`
|
|
5
|
+
* instances, following the grammar declared in `src/types.ts` and sourced
|
|
6
|
+
* verbatim from the verified spec:
|
|
7
|
+
* `docs/superpowers/plans/2026-06-23-festivals-decisions-and-spec.md`
|
|
8
|
+
* §4 "Verified v1 festival spec" (24 core festivals)
|
|
9
|
+
* §4b "Extended set" (Ekadashi / Sankashti Chaturthi / Chhath Puja)
|
|
10
|
+
*
|
|
11
|
+
* CALENDRICAL ONLY — no HSNA editorial copy (deity/significance/links live
|
|
12
|
+
* in web/ later). `meta.note` is used only to flag mapping imperfections.
|
|
13
|
+
*
|
|
14
|
+
* AUTHORITIES:
|
|
15
|
+
* • Sampradāya: Smārta (Drik Panchang default)
|
|
16
|
+
* • Month system: Pūrṇimānta (primary)
|
|
17
|
+
* • Month-name spellings match LUNAR_MONTH_NAMES in elements.ts exactly.
|
|
18
|
+
*/
|
|
19
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
20
|
+
// §4 — CORE RULES (24 festivals, calendar order)
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
22
|
+
/**
|
|
23
|
+
* The 24 individually named festivals from §4 of the spec. Ordered by 2026
|
|
24
|
+
* date (calendar year order, Jan–Dec).
|
|
25
|
+
*
|
|
26
|
+
* Month-name spellings must match the strings in `LUNAR_MONTH_NAMES`
|
|
27
|
+
* (elements.ts) exactly, since the evaluator normalizes and compares them:
|
|
28
|
+
* Chaitra · Vaishakha · Jyeshtha · Ashadha · Shravana · Bhadrapada ·
|
|
29
|
+
* Ashwina · Kartika · Margashirsha · Pausha · Magha · Phalguna
|
|
30
|
+
*/
|
|
31
|
+
export const CORE_RULES = [
|
|
32
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
33
|
+
// 1. Makar Sankranti — solar-ingress, rashi 9 (Makara = Capricorn, 270°)
|
|
34
|
+
// Spec: puṇya-kāla = 40 ghaṭis from ingress moment.
|
|
35
|
+
// Note: rashi 9 is Makara (0=Mesha, 9=Makara) per elements.ts solarIngress.
|
|
36
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
37
|
+
{
|
|
38
|
+
id: "makar-sankranti",
|
|
39
|
+
displayName: "Makar Sankranti",
|
|
40
|
+
month: { purnimanta: "Pausha" },
|
|
41
|
+
category: "solar",
|
|
42
|
+
observance: {
|
|
43
|
+
kind: "solar-ingress",
|
|
44
|
+
rashi: 9,
|
|
45
|
+
punyaKala: "after-moment-to-sunset",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
49
|
+
// 2. Vasant Panchami — Magha Shukla 5, pūrvāhna (forenoon)
|
|
50
|
+
// Spec: Panchami pervading forenoon (sunrise→midday).
|
|
51
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
52
|
+
{
|
|
53
|
+
id: "vasant-panchami",
|
|
54
|
+
displayName: "Vasant Panchami",
|
|
55
|
+
month: { purnimanta: "Magha" },
|
|
56
|
+
category: "lunar-tithi",
|
|
57
|
+
observance: {
|
|
58
|
+
kind: "tithi-pervades",
|
|
59
|
+
paksha: "shukla",
|
|
60
|
+
tithi: 5,
|
|
61
|
+
window: "purvahna",
|
|
62
|
+
precedence: "max-window-fraction",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
66
|
+
// 3. Maha Shivratri — Phalguna Krishna 14, niśīta (midnight window)
|
|
67
|
+
// Spec: Chaturdashi pervading niśīta.
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
69
|
+
{
|
|
70
|
+
id: "maha-shivratri",
|
|
71
|
+
displayName: "Maha Shivratri",
|
|
72
|
+
month: { purnimanta: "Phalguna" },
|
|
73
|
+
category: "lunar-tithi",
|
|
74
|
+
observance: {
|
|
75
|
+
kind: "tithi-pervades",
|
|
76
|
+
paksha: "krishna",
|
|
77
|
+
tithi: 14,
|
|
78
|
+
window: "nishita",
|
|
79
|
+
precedence: "max-window-fraction",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
83
|
+
// 4. Holika Dahan — Phalguna Shukla Pūrṇimā, pradoṣa, Bhadra-free
|
|
84
|
+
// Spec: Purnima in pradoṣa AND Bhadra-free (vishti karana exclusion).
|
|
85
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
86
|
+
{
|
|
87
|
+
id: "holika-dahan",
|
|
88
|
+
displayName: "Holika Dahan",
|
|
89
|
+
month: { purnimanta: "Phalguna" },
|
|
90
|
+
category: "lunar-tithi",
|
|
91
|
+
observance: {
|
|
92
|
+
kind: "tithi-pervades",
|
|
93
|
+
paksha: "shukla",
|
|
94
|
+
tithi: "purnima",
|
|
95
|
+
window: "pradosha",
|
|
96
|
+
precedence: "max-window-fraction",
|
|
97
|
+
avoidKarana: "vishti",
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
101
|
+
// 5. Holi (Dhulandi) — derived +1 day from Holika Dahan
|
|
102
|
+
// Spec: Chaitra Krishna 1 at sunrise (derived from Holika +1).
|
|
103
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
104
|
+
{
|
|
105
|
+
id: "holi",
|
|
106
|
+
displayName: "Holi (Dhulandi)",
|
|
107
|
+
month: { purnimanta: "Chaitra" },
|
|
108
|
+
category: "derived",
|
|
109
|
+
observance: {
|
|
110
|
+
kind: "derived",
|
|
111
|
+
from: "holika-dahan",
|
|
112
|
+
offsetDays: 1,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
116
|
+
// 6. Rama Navami — Chaitra Shukla 9, madhyāhna
|
|
117
|
+
// Spec: Navami pervading madhyāhna (Rama's birth).
|
|
118
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
119
|
+
{
|
|
120
|
+
id: "rama-navami",
|
|
121
|
+
displayName: "Rama Navami",
|
|
122
|
+
month: { purnimanta: "Chaitra" },
|
|
123
|
+
category: "lunar-tithi",
|
|
124
|
+
observance: {
|
|
125
|
+
kind: "tithi-pervades",
|
|
126
|
+
paksha: "shukla",
|
|
127
|
+
tithi: 9,
|
|
128
|
+
window: "madhyahna",
|
|
129
|
+
precedence: "max-window-fraction",
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
133
|
+
// 7. Hanuman Jayanti — Chaitra Shukla Pūrṇimā, sunrise
|
|
134
|
+
// Spec: Purnima at sunrise (purnima-vyāpti).
|
|
135
|
+
// PRECEDENCE: udaya. A sunrise-anchored vyāpti festival is observed on the
|
|
136
|
+
// day whose tithi is PRESENT AT SUNRISE (udaya-tithi), not the day with the
|
|
137
|
+
// larger window-fraction. In 2026 Pūrṇimā spans both Apr 1 and Apr 2
|
|
138
|
+
// sunrises with a *larger* fraction on Apr 1, yet Drik picks Apr 2 because
|
|
139
|
+
// Pūrṇimā prevails at Apr 2 sunrise — exactly the udaya rule.
|
|
140
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
141
|
+
{
|
|
142
|
+
id: "hanuman-jayanti",
|
|
143
|
+
displayName: "Hanuman Jayanti",
|
|
144
|
+
month: { purnimanta: "Chaitra" },
|
|
145
|
+
category: "lunar-tithi",
|
|
146
|
+
observance: {
|
|
147
|
+
kind: "tithi-pervades",
|
|
148
|
+
paksha: "shukla",
|
|
149
|
+
tithi: "purnima",
|
|
150
|
+
window: "sunrise",
|
|
151
|
+
precedence: "udaya",
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
155
|
+
// 8. Mesha Sankranti / Baisakhi — solar-ingress rashi 0 (Mesha = Aries)
|
|
156
|
+
// Spec: optional; sidereal Sun → Mesha (0°); solar new year.
|
|
157
|
+
// Marked extended:true per task brief ("optional").
|
|
158
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
159
|
+
{
|
|
160
|
+
id: "mesha-sankranti",
|
|
161
|
+
displayName: "Mesha Sankranti / Baisakhi",
|
|
162
|
+
month: { purnimanta: "Chaitra" },
|
|
163
|
+
category: "solar",
|
|
164
|
+
extended: true,
|
|
165
|
+
observance: {
|
|
166
|
+
kind: "solar-ingress",
|
|
167
|
+
rashi: 0,
|
|
168
|
+
punyaKala: "after-moment-to-sunset",
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
172
|
+
// 9. Akshaya Tritiya — Vaishakha Shukla 3, pūrvāhna
|
|
173
|
+
// Spec: Tritiya pervading forenoon; Rohini+Wed auspicious but not required.
|
|
174
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
175
|
+
{
|
|
176
|
+
id: "akshaya-tritiya",
|
|
177
|
+
displayName: "Akshaya Tritiya",
|
|
178
|
+
month: { purnimanta: "Vaishakha" },
|
|
179
|
+
category: "lunar-tithi",
|
|
180
|
+
observance: {
|
|
181
|
+
kind: "tithi-pervades",
|
|
182
|
+
paksha: "shukla",
|
|
183
|
+
tithi: 3,
|
|
184
|
+
window: "purvahna",
|
|
185
|
+
precedence: "max-window-fraction",
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
189
|
+
// 10. Guru Purnima — Ashadha Shukla Pūrṇimā, sunrise
|
|
190
|
+
// Spec: Purnima-vyāpti.
|
|
191
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
192
|
+
{
|
|
193
|
+
id: "guru-purnima",
|
|
194
|
+
displayName: "Guru Purnima",
|
|
195
|
+
month: { purnimanta: "Ashadha" },
|
|
196
|
+
category: "lunar-tithi",
|
|
197
|
+
observance: {
|
|
198
|
+
kind: "tithi-pervades",
|
|
199
|
+
paksha: "shukla",
|
|
200
|
+
tithi: "purnima",
|
|
201
|
+
window: "sunrise",
|
|
202
|
+
precedence: "max-window-fraction",
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
206
|
+
// 11. Raksha Bandhan — Shravana Shukla Pūrṇimā, aparāhna, Bhadra-free
|
|
207
|
+
// Spec: tie thread in aparāhna, Bhadra-free.
|
|
208
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
209
|
+
{
|
|
210
|
+
id: "raksha-bandhan",
|
|
211
|
+
displayName: "Raksha Bandhan",
|
|
212
|
+
month: { purnimanta: "Shravana" },
|
|
213
|
+
category: "lunar-tithi",
|
|
214
|
+
observance: {
|
|
215
|
+
kind: "tithi-pervades",
|
|
216
|
+
paksha: "shukla",
|
|
217
|
+
tithi: "purnima",
|
|
218
|
+
window: "aparahna",
|
|
219
|
+
precedence: "max-window-fraction",
|
|
220
|
+
avoidKarana: "vishti",
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
224
|
+
// 12. Krishna Janmashtami — Bhadrapada Krishna 8, niśīta
|
|
225
|
+
// Spec: Smārta: Ashtami at niśīta + Rohini preference (preferred, not required);
|
|
226
|
+
// Saptami-viddha OK (so no required filter).
|
|
227
|
+
// sampradaya:"smarta" explicit per spec.
|
|
228
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
229
|
+
{
|
|
230
|
+
id: "krishna-janmashtami",
|
|
231
|
+
displayName: "Krishna Janmashtami",
|
|
232
|
+
month: { purnimanta: "Bhadrapada" },
|
|
233
|
+
category: "lunar-tithi",
|
|
234
|
+
sampradaya: "smarta",
|
|
235
|
+
observance: {
|
|
236
|
+
kind: "tithi-pervades",
|
|
237
|
+
paksha: "krishna",
|
|
238
|
+
tithi: 8,
|
|
239
|
+
window: "nishita",
|
|
240
|
+
precedence: "max-window-fraction",
|
|
241
|
+
nakshatra: { name: "Rohini", window: "nishita", mode: "preferred" },
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
245
|
+
// 13. Ganesh Chaturthi — Bhadrapada Shukla 4, madhyāhna
|
|
246
|
+
// Spec: Chaturthi pervading madhyāhna.
|
|
247
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
248
|
+
{
|
|
249
|
+
id: "ganesh-chaturthi",
|
|
250
|
+
displayName: "Ganesh Chaturthi",
|
|
251
|
+
month: { purnimanta: "Bhadrapada" },
|
|
252
|
+
category: "lunar-tithi",
|
|
253
|
+
observance: {
|
|
254
|
+
kind: "tithi-pervades",
|
|
255
|
+
paksha: "shukla",
|
|
256
|
+
tithi: 4,
|
|
257
|
+
window: "madhyahna",
|
|
258
|
+
precedence: "max-window-fraction",
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
262
|
+
// 14. Navratri / Ghatasthapana (Sharadiya) — Ashwina Shukla 1, pūrvāhna
|
|
263
|
+
// Spec: Pratipadā in first ⅓ of day.
|
|
264
|
+
// Note: The spec also mentions nakshatra/yoga prohibitions (Chitra, Vaidhrti)
|
|
265
|
+
// as flags for 2026, but this is editorial/diagnostic, not a rule constraint
|
|
266
|
+
// in the grammar; not encoding here (Phase 4).
|
|
267
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
268
|
+
{
|
|
269
|
+
id: "sharadiya-navratri",
|
|
270
|
+
displayName: "Navratri / Ghatasthapana",
|
|
271
|
+
month: { purnimanta: "Ashwina" },
|
|
272
|
+
category: "lunar-tithi",
|
|
273
|
+
observance: {
|
|
274
|
+
kind: "tithi-pervades",
|
|
275
|
+
paksha: "shukla",
|
|
276
|
+
tithi: 1,
|
|
277
|
+
window: "purvahna",
|
|
278
|
+
// Ghaṭasthāpana is udaya-vyāpinī: observed on the day Pratipadā is present
|
|
279
|
+
// at sunrise. max-window-fraction coincides with udaya at New Delhi (the
|
|
280
|
+
// tithi sits in the evening there) but diverges at far-western longitudes
|
|
281
|
+
// where Pratipadā straddles sunrise — Calgary 2026 confirmed udaya matches
|
|
282
|
+
// Drik (Oct 11) while max-window-fraction wrongly gave Oct 10.
|
|
283
|
+
precedence: "udaya",
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
287
|
+
// 15. Durga Ashtami — Ashwina Shukla 8, sunrise
|
|
288
|
+
// Spec: Ashtami at sunrise.
|
|
289
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
290
|
+
{
|
|
291
|
+
id: "durga-ashtami",
|
|
292
|
+
displayName: "Durga Ashtami",
|
|
293
|
+
month: { purnimanta: "Ashwina" },
|
|
294
|
+
category: "lunar-tithi",
|
|
295
|
+
observance: {
|
|
296
|
+
kind: "tithi-pervades",
|
|
297
|
+
paksha: "shukla",
|
|
298
|
+
tithi: 8,
|
|
299
|
+
window: "sunrise",
|
|
300
|
+
precedence: "max-window-fraction",
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
304
|
+
// 16. Maha Navami — Ashwina Shukla 9, sunrise
|
|
305
|
+
// Spec: Navami-vyāpti → PRECEDENCE: udaya (present-at-sunrise), the same
|
|
306
|
+
// sunrise-anchored udaya rule used by Hanuman Jayanti. NOTE: Durga Ashtami
|
|
307
|
+
// (the adjacent sunrise festival) intentionally stays on
|
|
308
|
+
// max-window-fraction — it resolves correctly that way for 2026; do NOT
|
|
309
|
+
// switch it to udaya.
|
|
310
|
+
// KNOWN EXPECTED-DIFF (2026): Drik publishes Maha Navami on Oct 19, the
|
|
311
|
+
// SAME civil day as Maha Ashtami, with Sandhi Pūjā 10:27–11:15 IST. In
|
|
312
|
+
// 2026 Navami runs Oct 19 10:52 → Oct 20 12:51 IST, so it prevails at the
|
|
313
|
+
// Oct 20 sunrise, NOT Oct 19's (Oct 19 sunrise carries Ashtami). Pure
|
|
314
|
+
// udaya-tithi therefore yields Oct 20. Drik's Oct 19 comes from the
|
|
315
|
+
// Durga-Pūjā Sandhi / Navami-conjoined-with-Ashtami convention (Navami
|
|
316
|
+
// observed on the Ashtami-udaya day when the Sandhi junction falls that
|
|
317
|
+
// morning) — a Durga-Pūjā-specific rule NOT expressible in the generic
|
|
318
|
+
// tithi-pervasion grammar. Left as a documented +1 expected-diff rather
|
|
319
|
+
// than hardcoded or hacked. (Vijayadashami/aparāhna on Oct 20 still
|
|
320
|
+
// matches Drik.)
|
|
321
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
322
|
+
{
|
|
323
|
+
id: "maha-navami",
|
|
324
|
+
displayName: "Maha Navami",
|
|
325
|
+
month: { purnimanta: "Ashwina" },
|
|
326
|
+
category: "lunar-tithi",
|
|
327
|
+
observance: {
|
|
328
|
+
kind: "tithi-pervades",
|
|
329
|
+
paksha: "shukla",
|
|
330
|
+
tithi: 9,
|
|
331
|
+
window: "sunrise",
|
|
332
|
+
precedence: "udaya",
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
336
|
+
// 17. Vijayadashami (Dussehra) — Ashwina Shukla 10, aparāhna
|
|
337
|
+
// Spec: Dashami pervading aparāhna.
|
|
338
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
339
|
+
{
|
|
340
|
+
id: "vijayadashami",
|
|
341
|
+
displayName: "Vijayadashami (Dussehra)",
|
|
342
|
+
month: { purnimanta: "Ashwina" },
|
|
343
|
+
category: "lunar-tithi",
|
|
344
|
+
observance: {
|
|
345
|
+
kind: "tithi-pervades",
|
|
346
|
+
paksha: "shukla",
|
|
347
|
+
tithi: 10,
|
|
348
|
+
window: "aparahna",
|
|
349
|
+
precedence: "max-window-fraction",
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
353
|
+
// 18. Karva Chauth — Kartika Krishna 4, moonrise
|
|
354
|
+
// Spec: Chaturthi live at moonrise; fast broken at chandrodaya.
|
|
355
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
356
|
+
{
|
|
357
|
+
id: "karva-chauth",
|
|
358
|
+
displayName: "Karva Chauth",
|
|
359
|
+
month: { purnimanta: "Kartika" },
|
|
360
|
+
category: "moonrise",
|
|
361
|
+
observance: {
|
|
362
|
+
kind: "moonrise",
|
|
363
|
+
paksha: "krishna",
|
|
364
|
+
tithi: 4,
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
368
|
+
// 19. Dhanteras — Kartika Krishna 13, pradoṣa
|
|
369
|
+
// Spec: Trayodashi pervading pradoṣa.
|
|
370
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
371
|
+
{
|
|
372
|
+
id: "dhanteras",
|
|
373
|
+
displayName: "Dhanteras",
|
|
374
|
+
month: { purnimanta: "Kartika" },
|
|
375
|
+
category: "lunar-tithi",
|
|
376
|
+
observance: {
|
|
377
|
+
kind: "tithi-pervades",
|
|
378
|
+
paksha: "krishna",
|
|
379
|
+
tithi: 13,
|
|
380
|
+
window: "pradosha",
|
|
381
|
+
precedence: "max-window-fraction",
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
385
|
+
// 20. Naraka Chaturdashi (Choti Diwali) — Kartika Krishna 14
|
|
386
|
+
// Spec: Abhyaṅga-snāna at moonrise-before-sunrise with Chaturdashi current.
|
|
387
|
+
// IMPERFECT FIT FLAG: The grammar has no "moonrise-before-sunrise" kind.
|
|
388
|
+
// Mapped to `moonrise` (Kartika Krishna 14) as the closest available
|
|
389
|
+
// primitive. The evaluator will find the day whose moonrise falls within
|
|
390
|
+
// the Chaturdashi interval — which for a pre-dawn moonrise will be the
|
|
391
|
+
// same civil day as the Abhyanga Snana. Phase 4 should add a dedicated
|
|
392
|
+
// "moonrise-before-sunrise" window or a "pratahkala-moonrise" kind.
|
|
393
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
394
|
+
{
|
|
395
|
+
id: "naraka-chaturdashi",
|
|
396
|
+
displayName: "Naraka Chaturdashi (Choti Diwali)",
|
|
397
|
+
month: { purnimanta: "Kartika" },
|
|
398
|
+
category: "moonrise",
|
|
399
|
+
observance: {
|
|
400
|
+
kind: "moonrise",
|
|
401
|
+
paksha: "krishna",
|
|
402
|
+
tithi: 14,
|
|
403
|
+
},
|
|
404
|
+
meta: {
|
|
405
|
+
note: "IMPERFECT FIT: spec requires moonrise-before-sunrise (Abhyanga Snana). Mapped to `moonrise` kind (nearest available). Phase 4 should add a moonrise-before-sunrise primitive or dedicate a pre-dawn window.",
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
409
|
+
// 21. Diwali / Lakshmi Puja — Kartika Krishna Amāvāsyā, pradoṣa
|
|
410
|
+
// Spec: 2-day tie-break: day where Amāvāsyā pervades pradoṣa
|
|
411
|
+
// (+ sthira/Vṛṣabha lagna — editorial, not in grammar).
|
|
412
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
413
|
+
{
|
|
414
|
+
id: "diwali-lakshmi-puja",
|
|
415
|
+
displayName: "Diwali / Lakshmi Puja",
|
|
416
|
+
month: { purnimanta: "Kartika" },
|
|
417
|
+
category: "lunar-tithi",
|
|
418
|
+
observance: {
|
|
419
|
+
kind: "tithi-pervades",
|
|
420
|
+
paksha: "krishna",
|
|
421
|
+
tithi: "amavasya",
|
|
422
|
+
window: "pradosha",
|
|
423
|
+
precedence: "max-window-fraction",
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
427
|
+
// 22. Govardhan Puja / Annakut — Kartika Shukla 1, prātaḥ-kāla
|
|
428
|
+
// Spec: Pratipadā at forenoon.
|
|
429
|
+
// Note: "pratahkala" maps to the sunriseWindow function in time.ts
|
|
430
|
+
// (alias of sunrise kāla). This is correct per §5 and the kāla definitions.
|
|
431
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
432
|
+
{
|
|
433
|
+
id: "govardhan-puja",
|
|
434
|
+
displayName: "Govardhan Puja / Annakut",
|
|
435
|
+
month: { purnimanta: "Kartika" },
|
|
436
|
+
category: "lunar-tithi",
|
|
437
|
+
observance: {
|
|
438
|
+
kind: "tithi-pervades",
|
|
439
|
+
paksha: "shukla",
|
|
440
|
+
tithi: 1,
|
|
441
|
+
window: "pratahkala",
|
|
442
|
+
precedence: "max-window-fraction",
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
446
|
+
// 23. Bhai Dooj — Kartika Shukla 2, aparāhna
|
|
447
|
+
// Spec: Dwitiya pervading aparāhna.
|
|
448
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
449
|
+
{
|
|
450
|
+
id: "bhai-dooj",
|
|
451
|
+
displayName: "Bhai Dooj",
|
|
452
|
+
month: { purnimanta: "Kartika" },
|
|
453
|
+
category: "lunar-tithi",
|
|
454
|
+
observance: {
|
|
455
|
+
kind: "tithi-pervades",
|
|
456
|
+
paksha: "shukla",
|
|
457
|
+
tithi: 2,
|
|
458
|
+
window: "aparahna",
|
|
459
|
+
precedence: "max-window-fraction",
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
463
|
+
// 24. Gita Jayanti (Mokshada Ekadashi) — Margashirsha Shukla 11, sunrise
|
|
464
|
+
// Spec: Ekadashi-vyāpti; Gita Jayanti date coincides with the Ekadashi
|
|
465
|
+
// (Smārta / Vaishnava split is on the underlying vrata, not this date).
|
|
466
|
+
// Also generated by the ekadashi() generator below, but kept here as a
|
|
467
|
+
// named core festival per §4. The two rules produce the same date.
|
|
468
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
469
|
+
{
|
|
470
|
+
id: "gita-jayanti",
|
|
471
|
+
displayName: "Gita Jayanti (Mokshada Ekadashi)",
|
|
472
|
+
month: { purnimanta: "Margashirsha" },
|
|
473
|
+
category: "lunar-tithi",
|
|
474
|
+
observance: {
|
|
475
|
+
kind: "tithi-pervades",
|
|
476
|
+
paksha: "shukla",
|
|
477
|
+
tithi: 11,
|
|
478
|
+
window: "sunrise",
|
|
479
|
+
precedence: "udaya",
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
];
|
|
483
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
484
|
+
// §4b — EXTENDED SET GENERATORS
|
|
485
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
486
|
+
/**
|
|
487
|
+
* The 12 regular pūrṇimānta months in calendar order (amānta names;
|
|
488
|
+
* pūrṇimānta derivation is handled by the evaluator from `elements.ts`).
|
|
489
|
+
*
|
|
490
|
+
* Month-name spellings match LUNAR_MONTH_NAMES in elements.ts exactly.
|
|
491
|
+
*/
|
|
492
|
+
const PURNIMANTA_MONTHS = [
|
|
493
|
+
"Chaitra", // 0
|
|
494
|
+
"Vaishakha", // 1
|
|
495
|
+
"Jyeshtha", // 2
|
|
496
|
+
"Ashadha", // 3
|
|
497
|
+
"Shravana", // 4
|
|
498
|
+
"Bhadrapada", // 5
|
|
499
|
+
"Ashwina", // 6
|
|
500
|
+
"Kartika", // 7
|
|
501
|
+
"Margashirsha", // 8
|
|
502
|
+
"Pausha", // 9
|
|
503
|
+
"Magha", // 10
|
|
504
|
+
"Phalguna", // 11
|
|
505
|
+
];
|
|
506
|
+
/**
|
|
507
|
+
* Canonical Ekadashi names for each (month, paksha) pair where well known.
|
|
508
|
+
* Indexed as `EKADASHI_NAMES[monthIndex][paksha]`.
|
|
509
|
+
* Source: Drik Panchang Ekadashi calendar; widely attested in tradition.
|
|
510
|
+
* Pairs not listed here fall back to "<Month> <Paksha> Ekadashi".
|
|
511
|
+
*
|
|
512
|
+
* Note: this list covers the 12 regular months only.
|
|
513
|
+
* Adhika months (Padmini = adhika Shukla Ekadashi, Parama = adhika Krishna
|
|
514
|
+
* Ekadashi) use their own canonical names.
|
|
515
|
+
*/
|
|
516
|
+
// NOTE on krishna names: the engine labels months pūrṇimānta, so the Krishna
|
|
517
|
+
// pakṣa of month M falls within month M (the fortnight before M's Pūrṇimā). The
|
|
518
|
+
// canonical Ekadashi names follow that same pūrṇimānta attribution — e.g. Māgha
|
|
519
|
+
// Kṛṣṇa = Ṣaṭtilā, Phālguna Kṛṣṇa = Vijayā — which is what Drik Panchang lists.
|
|
520
|
+
// (An earlier version keyed the krishna column one month early, an amānta-style
|
|
521
|
+
// offset, which mislabelled all twelve Krishna Ekadashis.)
|
|
522
|
+
const EKADASHI_NAMES = {
|
|
523
|
+
Chaitra: { shukla: "Kamada Ekadashi", krishna: "Papamochani Ekadashi" },
|
|
524
|
+
Vaishakha: { shukla: "Mohini Ekadashi", krishna: "Varuthini Ekadashi" },
|
|
525
|
+
Jyeshtha: { shukla: "Nirjala Ekadashi", krishna: "Apara Ekadashi" },
|
|
526
|
+
Ashadha: { shukla: "Devshayani Ekadashi", krishna: "Yogini Ekadashi" },
|
|
527
|
+
Shravana: { shukla: "Putrada Ekadashi", krishna: "Kamika Ekadashi" },
|
|
528
|
+
Bhadrapada: { shukla: "Parsva Ekadashi", krishna: "Aja Ekadashi" },
|
|
529
|
+
Ashwina: { shukla: "Papankusha Ekadashi", krishna: "Indira Ekadashi" },
|
|
530
|
+
Kartika: { shukla: "Devutthana Ekadashi", krishna: "Rama Ekadashi" },
|
|
531
|
+
Margashirsha: { shukla: "Mokshada Ekadashi", krishna: "Utpanna Ekadashi" },
|
|
532
|
+
Pausha: { shukla: "Putrada Ekadashi", krishna: "Saphala Ekadashi" },
|
|
533
|
+
Magha: { shukla: "Jaya Ekadashi", krishna: "Sat-tila Ekadashi" },
|
|
534
|
+
Phalguna: { shukla: "Amalaki Ekadashi", krishna: "Vijaya Ekadashi" },
|
|
535
|
+
};
|
|
536
|
+
/**
|
|
537
|
+
* Canonical names for Ekadashis falling in an Adhika (leap) month.
|
|
538
|
+
* Source: tradition / Drik Panchang.
|
|
539
|
+
*/
|
|
540
|
+
const ADHIKA_EKADASHI_NAMES = {
|
|
541
|
+
shukla: "Padmini Ekadashi",
|
|
542
|
+
krishna: "Parama Ekadashi",
|
|
543
|
+
};
|
|
544
|
+
/**
|
|
545
|
+
* Generate all Ekadashi rules for `year`.
|
|
546
|
+
*
|
|
547
|
+
* Rule (§4b, Smārta householder):
|
|
548
|
+
* Observe the day on which Ekadashi prevails at sunrise (udaya-tithi).
|
|
549
|
+
* When two candidate days arise (Dashami-vedha), take the first — Smārta
|
|
550
|
+
* householders accept Dashami-vedha (the Vaishnava arunodaya/Dashami-vedha
|
|
551
|
+
* skip is NOT used here).
|
|
552
|
+
*
|
|
553
|
+
* Count (2026): 24 Ekadashis total.
|
|
554
|
+
* 12 regular months × 2 pakshas = 24.
|
|
555
|
+
* The Adhika Jyeshtha in 2026 contributes Padmini (Shukla-11) + Parama
|
|
556
|
+
* (Krishna-11), which ARE included in the 24.
|
|
557
|
+
*
|
|
558
|
+
* Implementation strategy: generate rules for all 12 regular months (both
|
|
559
|
+
* pakshas), PLUS the Adhika Jyeshtha pair. The evaluator's nija-preference
|
|
560
|
+
* logic (findTithiIntervalInMonth) will resolve the regular "Jyeshtha" rules
|
|
561
|
+
* to the Nija lunation, while the adhika rules get their own month label
|
|
562
|
+
* "Adhika Jyeshtha".
|
|
563
|
+
*
|
|
564
|
+
* Precedence "udaya" → Ekadashi present at sunrise (window start) wins.
|
|
565
|
+
*/
|
|
566
|
+
export function ekadashiRules(year) {
|
|
567
|
+
const rules = [];
|
|
568
|
+
// 12 regular months, both pakshas.
|
|
569
|
+
for (const month of PURNIMANTA_MONTHS) {
|
|
570
|
+
const names = EKADASHI_NAMES[month];
|
|
571
|
+
// Shukla Ekadashi (tithi 11 of shukla paksha).
|
|
572
|
+
rules.push({
|
|
573
|
+
id: `ekadashi-${month.toLowerCase()}-shukla`,
|
|
574
|
+
displayName: names?.shukla ?? `${month} Shukla Ekadashi`,
|
|
575
|
+
month: { purnimanta: month },
|
|
576
|
+
category: "lunar-tithi",
|
|
577
|
+
extended: true,
|
|
578
|
+
observance: {
|
|
579
|
+
kind: "tithi-pervades",
|
|
580
|
+
paksha: "shukla",
|
|
581
|
+
tithi: 11,
|
|
582
|
+
window: "sunrise",
|
|
583
|
+
precedence: "udaya",
|
|
584
|
+
},
|
|
585
|
+
});
|
|
586
|
+
// Krishna Ekadashi (tithi 11 of krishna paksha).
|
|
587
|
+
// Note: in pūrṇimānta reckoning, the Krishna paksha of e.g. Chaitra
|
|
588
|
+
// belongs to Chaitra month (the fortnight BEFORE Chaitra Shukla).
|
|
589
|
+
// The evaluator's absoluteTithi + findTithiIntervalInMonth handles this
|
|
590
|
+
// correctly via the pūrṇimānta label on the tithi midpoint.
|
|
591
|
+
rules.push({
|
|
592
|
+
id: `ekadashi-${month.toLowerCase()}-krishna`,
|
|
593
|
+
displayName: names?.krishna ?? `${month} Krishna Ekadashi`,
|
|
594
|
+
month: { purnimanta: month },
|
|
595
|
+
category: "lunar-tithi",
|
|
596
|
+
extended: true,
|
|
597
|
+
observance: {
|
|
598
|
+
kind: "tithi-pervades",
|
|
599
|
+
paksha: "krishna",
|
|
600
|
+
tithi: 11,
|
|
601
|
+
window: "sunrise",
|
|
602
|
+
precedence: "udaya",
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
// Adhika Jyeshtha pair (present in 2026; generator is year-aware via
|
|
607
|
+
// the month label "Adhika Jyeshtha" which the evaluator normalizes and
|
|
608
|
+
// matches against the adhika lunation in the year's new-moon scan).
|
|
609
|
+
// These are only emitted when an adhika Jyeshtha is expected. For now we
|
|
610
|
+
// always emit them; the evaluator will return an empty date (with a
|
|
611
|
+
// diagnostic) for years without an adhika Jyeshtha, and the integration
|
|
612
|
+
// test checks the 2026 count. In a future revision, emit conditionally.
|
|
613
|
+
rules.push({
|
|
614
|
+
id: "ekadashi-adhika-jyeshtha-shukla",
|
|
615
|
+
displayName: ADHIKA_EKADASHI_NAMES.shukla,
|
|
616
|
+
month: { purnimanta: "Adhika Jyeshtha" },
|
|
617
|
+
category: "lunar-tithi",
|
|
618
|
+
extended: true,
|
|
619
|
+
meta: {
|
|
620
|
+
note: "Adhika Jyeshtha Shukla Ekadashi. Present in 2026; evaluator returns empty date with diagnostic in non-adhika years.",
|
|
621
|
+
},
|
|
622
|
+
observance: {
|
|
623
|
+
kind: "tithi-pervades",
|
|
624
|
+
paksha: "shukla",
|
|
625
|
+
tithi: 11,
|
|
626
|
+
window: "sunrise",
|
|
627
|
+
precedence: "udaya",
|
|
628
|
+
},
|
|
629
|
+
});
|
|
630
|
+
rules.push({
|
|
631
|
+
id: "ekadashi-adhika-jyeshtha-krishna",
|
|
632
|
+
displayName: ADHIKA_EKADASHI_NAMES.krishna,
|
|
633
|
+
month: { purnimanta: "Adhika Jyeshtha" },
|
|
634
|
+
category: "lunar-tithi",
|
|
635
|
+
extended: true,
|
|
636
|
+
meta: {
|
|
637
|
+
note: "Adhika Jyeshtha Krishna Ekadashi. Present in 2026; evaluator returns empty date with diagnostic in non-adhika years.",
|
|
638
|
+
},
|
|
639
|
+
observance: {
|
|
640
|
+
kind: "tithi-pervades",
|
|
641
|
+
paksha: "krishna",
|
|
642
|
+
tithi: 11,
|
|
643
|
+
window: "sunrise",
|
|
644
|
+
precedence: "udaya",
|
|
645
|
+
},
|
|
646
|
+
});
|
|
647
|
+
void year; // year parameter reserved for future conditional adhika emission
|
|
648
|
+
return rules;
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Generate Sankashti Chaturthi rules for `year`.
|
|
652
|
+
*
|
|
653
|
+
* Rule (§4b): Each month's Krishna-paksha Chaturthi, the day Chaturthi is
|
|
654
|
+
* current at moonrise (moonrise-vyāpti). If Chaturthi touches no moonrise,
|
|
655
|
+
* falls on the day whose moonrise is nearest to the Chaturthi interval.
|
|
656
|
+
* Kind: `moonrise` (Kartika Krishna 4 is the model; this extends to all months).
|
|
657
|
+
*
|
|
658
|
+
* Note: Angāraki (when Sankashti falls on a Tuesday) is an editorial flag,
|
|
659
|
+
* not a rule-selection criterion — not encoded here.
|
|
660
|
+
*
|
|
661
|
+
* Count (2026): 13 Sankashti Chaturthis (Adhika Jyeshtha contributes one extra).
|
|
662
|
+
*
|
|
663
|
+
* Like ekadashiRules, always emits the Adhika Jyeshtha entry; the evaluator
|
|
664
|
+
* handles years without an adhika month gracefully (empty date + diagnostic).
|
|
665
|
+
*/
|
|
666
|
+
export function sankashtiRules(year) {
|
|
667
|
+
const rules = [];
|
|
668
|
+
for (const month of PURNIMANTA_MONTHS) {
|
|
669
|
+
rules.push({
|
|
670
|
+
id: `sankashti-chaturthi-${month.toLowerCase()}`,
|
|
671
|
+
displayName: `Sankashti Chaturthi (${month})`,
|
|
672
|
+
month: { purnimanta: month },
|
|
673
|
+
category: "moonrise",
|
|
674
|
+
extended: true,
|
|
675
|
+
observance: {
|
|
676
|
+
kind: "moonrise",
|
|
677
|
+
paksha: "krishna",
|
|
678
|
+
tithi: 4,
|
|
679
|
+
},
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
// Adhika Jyeshtha Sankashti (present in 2026).
|
|
683
|
+
rules.push({
|
|
684
|
+
id: "sankashti-chaturthi-adhika-jyeshtha",
|
|
685
|
+
displayName: "Sankashti Chaturthi (Adhika Jyeshtha)",
|
|
686
|
+
month: { purnimanta: "Adhika Jyeshtha" },
|
|
687
|
+
category: "moonrise",
|
|
688
|
+
extended: true,
|
|
689
|
+
meta: {
|
|
690
|
+
note: "Adhika Jyeshtha Sankashti Chaturthi. Present in 2026; evaluator returns empty date with diagnostic in non-adhika years.",
|
|
691
|
+
},
|
|
692
|
+
observance: {
|
|
693
|
+
kind: "moonrise",
|
|
694
|
+
paksha: "krishna",
|
|
695
|
+
tithi: 4,
|
|
696
|
+
},
|
|
697
|
+
});
|
|
698
|
+
void year;
|
|
699
|
+
return rules;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* The single Chhath Puja rule (§4b): anchored on Kartika Shukla Shashthi.
|
|
703
|
+
*
|
|
704
|
+
* Spec: main day = Shashthi prevails at SUNSET (Sandhya Arghya).
|
|
705
|
+
* Grammar kind: `solar-arghya` (tithi at sunset + next sunrise).
|
|
706
|
+
* This is a clean mapping: solar-arghya resolves to the day whose sunset
|
|
707
|
+
* falls within the Shashthi interval (Sandhya Arghya = evening offering),
|
|
708
|
+
* and records the next morning's sunrise (Uṣā Arghya).
|
|
709
|
+
*
|
|
710
|
+
* The 4-day festival (Nahay Khay → Kharna → Shashthi → Saptami) is not
|
|
711
|
+
* modelled here as separate rules; only the anchor day is captured.
|
|
712
|
+
*/
|
|
713
|
+
export const CHHATH_RULE = {
|
|
714
|
+
id: "chhath-puja",
|
|
715
|
+
displayName: "Chhath Puja (Sandhya Arghya)",
|
|
716
|
+
month: { purnimanta: "Kartika" },
|
|
717
|
+
category: "lunar-tithi",
|
|
718
|
+
extended: true,
|
|
719
|
+
observance: {
|
|
720
|
+
kind: "solar-arghya",
|
|
721
|
+
paksha: "shukla",
|
|
722
|
+
tithi: 6,
|
|
723
|
+
},
|
|
724
|
+
};
|
|
725
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
726
|
+
// Recurring monthly vratas (§4c) — Pradoṣa, Masik Śivarātri, Pūrṇimā,
|
|
727
|
+
// Amāvāsyā, and the minor (non-Makar/Mesha) Sankrāntis.
|
|
728
|
+
//
|
|
729
|
+
// The mechanism already exists (tithi-pervades / solar-ingress); these supply
|
|
730
|
+
// the rule DATA. Each generator mirrors ekadashiRules: the 12 regular months
|
|
731
|
+
// plus the Adhika Jyeshtha entries (present in 2026), which the evaluator
|
|
732
|
+
// resolves to empty in non-adhika years.
|
|
733
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
734
|
+
const PAKSHAS = ["shukla", "krishna"];
|
|
735
|
+
const ADHIKA = "Adhika Jyeshtha";
|
|
736
|
+
const ADHIKA_NOTE = "Adhika-month entry; resolves to empty with a diagnostic in non-adhika years.";
|
|
737
|
+
/** Slugify a month label for an id: lower-case, spaces → hyphens. */
|
|
738
|
+
const monthSlug = (month) => month.toLowerCase().replace(/\s+/g, "-");
|
|
739
|
+
/**
|
|
740
|
+
* Shared scaffold for the recurring monthly vratas. Emits one rule per
|
|
741
|
+
* pūrṇimānta month plus the trailing Adhika Jyeṣṭha entry (present in 2026; the
|
|
742
|
+
* evaluator resolves it to an empty date with a diagnostic in non-adhika
|
|
743
|
+
* years). `build(month, slug, adhika)` returns the rule(s) for one month —
|
|
744
|
+
* `adhika` is true only on the Adhika pass, so callers attach `ADHIKA_NOTE`
|
|
745
|
+
* (and any extra pakṣa fan-out) themselves. This collapses four near-identical
|
|
746
|
+
* push-loop bodies into one.
|
|
747
|
+
*/
|
|
748
|
+
function monthlyVrataRules(build) {
|
|
749
|
+
const out = [];
|
|
750
|
+
for (const month of PURNIMANTA_MONTHS)
|
|
751
|
+
out.push(...build(month, monthSlug(month), false));
|
|
752
|
+
out.push(...build(ADHIKA, monthSlug(ADHIKA), true));
|
|
753
|
+
return out;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Pradoṣa Vrata — Trayodaśī (13) pervading the pradoṣa (post-sunset) window, in
|
|
757
|
+
* both pakṣas of every month. The (S)/(K) suffix marks śukla/kṛṣṇa.
|
|
758
|
+
*/
|
|
759
|
+
export function pradoshRules(year) {
|
|
760
|
+
void year;
|
|
761
|
+
return monthlyVrataRules((month, slug, adhika) => PAKSHAS.map((paksha) => ({
|
|
762
|
+
id: `pradosh-${slug}-${paksha}`,
|
|
763
|
+
displayName: `Pradosh Vrat (${paksha === "shukla" ? "S" : "K"}, ${month})`,
|
|
764
|
+
month: { purnimanta: month },
|
|
765
|
+
category: "lunar-tithi",
|
|
766
|
+
extended: true,
|
|
767
|
+
...(adhika ? { meta: { note: ADHIKA_NOTE } } : {}),
|
|
768
|
+
observance: { kind: "tithi-pervades", paksha, tithi: 13, window: "pradosha", precedence: "max-window-fraction" },
|
|
769
|
+
})));
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Masik Śivarātri — Kṛṣṇa Caturdaśī (14) in the niśīta (midnight) window.
|
|
773
|
+
*
|
|
774
|
+
* `fallback: "nearest-window"`: at far-western longitudes the Caturdaśī can
|
|
775
|
+
* straddle two midnights without covering either niśīta (it ends ~1h before the
|
|
776
|
+
* later one and starts after the earlier one), so no day pervades. The vrata
|
|
777
|
+
* still occurs — on the day the Caturdaśī is current going into the night — so
|
|
778
|
+
* we keep the candidate whose niśīta is nearest the tithi instead of dropping
|
|
779
|
+
* the date. (Closes the 2 undated Calgary 2026 entries; no effect where a day
|
|
780
|
+
* does pervade, i.e. New Delhi and the other 11 months.)
|
|
781
|
+
*/
|
|
782
|
+
export function masikShivaratriRules(year) {
|
|
783
|
+
void year;
|
|
784
|
+
return monthlyVrataRules((month, slug, adhika) => [{
|
|
785
|
+
id: `masik-shivaratri-${slug}`,
|
|
786
|
+
displayName: `Masik Shivaratri (${month})`,
|
|
787
|
+
month: { purnimanta: month },
|
|
788
|
+
category: "lunar-tithi",
|
|
789
|
+
extended: true,
|
|
790
|
+
...(adhika ? { meta: { note: ADHIKA_NOTE } } : {}),
|
|
791
|
+
observance: { kind: "tithi-pervades", paksha: "krishna", tithi: 14, window: "nishita", precedence: "max-window-fraction", fallback: "nearest-window" },
|
|
792
|
+
}]);
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Pūrṇimā Vrata — the full-moon day of every pūrṇimānta month. The vrata is kept
|
|
796
|
+
* on the day the full Moon is up during Pūrṇimā (moonrise-vyāpti), which is the
|
|
797
|
+
* day panchāṅgas list as "Purnima Vrat" (distinct from the next-morning
|
|
798
|
+
* snāna-dāna Pūrṇimā).
|
|
799
|
+
*
|
|
800
|
+
* KNOWN ISSUE (vs Drik Panchang Calgary 2026): this fires a day early in ~3/13
|
|
801
|
+
* months (e.g. Jyeṣṭha Jun 28 vs DP Jun 29) when Pūrṇimā begins the prior
|
|
802
|
+
* evening. A plain udaya rule overcorrects (drops months where Pūrṇimā never
|
|
803
|
+
* touches a sunrise, and pushes others a day late), so the correct fix needs a
|
|
804
|
+
* proper udaya-with-fallback / vedha policy — tracked, not yet implemented.
|
|
805
|
+
*/
|
|
806
|
+
export function purnimaVratRules(year) {
|
|
807
|
+
void year;
|
|
808
|
+
return monthlyVrataRules((month, slug, adhika) => [{
|
|
809
|
+
id: `purnima-vrat-${slug}`,
|
|
810
|
+
displayName: `${month} Purnima Vrat`,
|
|
811
|
+
month: { purnimanta: month },
|
|
812
|
+
category: "moonrise",
|
|
813
|
+
extended: true,
|
|
814
|
+
...(adhika ? { meta: { note: ADHIKA_NOTE } } : {}),
|
|
815
|
+
observance: { kind: "moonrise", paksha: "shukla", tithi: "purnima" },
|
|
816
|
+
}]);
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Pūrṇimā snāna-dāna — the full-moon day Drik lists as "X Purnima" (distinct
|
|
820
|
+
* from the moonrise "Purnima Vrat" above). It is the day Pūrṇimā prevails at
|
|
821
|
+
* sunrise (udaya), when the morning snāna/dāna is performed; at far-western
|
|
822
|
+
* longitudes this is typically the civil day AFTER the vrat. `nearest-window`
|
|
823
|
+
* covers the rare month whose Pūrṇimā touches no sunrise.
|
|
824
|
+
*/
|
|
825
|
+
export function purnimaSnanaRules(year) {
|
|
826
|
+
void year;
|
|
827
|
+
return monthlyVrataRules((month, slug, adhika) => [{
|
|
828
|
+
id: `purnima-snana-${slug}`,
|
|
829
|
+
displayName: `${month} Purnima (Snana-Dana)`,
|
|
830
|
+
month: { purnimanta: month },
|
|
831
|
+
category: "lunar-tithi",
|
|
832
|
+
extended: true,
|
|
833
|
+
...(adhika ? { meta: { note: ADHIKA_NOTE } } : {}),
|
|
834
|
+
observance: { kind: "tithi-pervades", paksha: "shukla", tithi: "purnima", window: "sunrise", precedence: "udaya", fallback: "nearest-window" },
|
|
835
|
+
}]);
|
|
836
|
+
}
|
|
837
|
+
/** Amāvāsyā — the new-moon (amāvāsyā) day of every pūrṇimānta month. */
|
|
838
|
+
export function amavasyaRules(year) {
|
|
839
|
+
void year;
|
|
840
|
+
return monthlyVrataRules((month, slug, adhika) => [{
|
|
841
|
+
id: `amavasya-${slug}`,
|
|
842
|
+
displayName: `${month} Amavasya`,
|
|
843
|
+
month: { purnimanta: month },
|
|
844
|
+
category: "lunar-tithi",
|
|
845
|
+
extended: true,
|
|
846
|
+
...(adhika ? { meta: { note: ADHIKA_NOTE } } : {}),
|
|
847
|
+
observance: { kind: "tithi-pervades", paksha: "krishna", tithi: "amavasya", window: "sunrise", precedence: "max-window-fraction" },
|
|
848
|
+
}]);
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* The ten minor Sankrāntis (the Sun's sidereal ingress into each rāśi), i.e.
|
|
852
|
+
* all twelve except Makara and Meṣa, which are CORE festivals. `punyaKala`
|
|
853
|
+
* follows Makar Sankranti's convention.
|
|
854
|
+
*/
|
|
855
|
+
export function sankrantiRules(year) {
|
|
856
|
+
// [displayName, id-suffix, rāśi index]; rāśi 0 = Mesha … 11 = Mīna.
|
|
857
|
+
const minor = [
|
|
858
|
+
["Kumbha Sankranti", "kumbha", 10],
|
|
859
|
+
["Meena Sankranti", "meena", 11],
|
|
860
|
+
["Vrishabha Sankranti", "vrishabha", 1],
|
|
861
|
+
["Mithuna Sankranti", "mithuna", 2],
|
|
862
|
+
["Karka Sankranti", "karka", 3],
|
|
863
|
+
["Simha Sankranti", "simha", 4],
|
|
864
|
+
["Kanya Sankranti", "kanya", 5],
|
|
865
|
+
["Tula Sankranti", "tula", 6],
|
|
866
|
+
["Vrishchika Sankranti", "vrishchika", 7],
|
|
867
|
+
["Dhanu Sankranti", "dhanu", 8],
|
|
868
|
+
];
|
|
869
|
+
void year;
|
|
870
|
+
// `month` is omitted: solar-ingress keys on the rāśi, not a lunar-month label.
|
|
871
|
+
return minor.map(([displayName, slug, rashi]) => ({
|
|
872
|
+
id: `sankranti-${slug}`,
|
|
873
|
+
displayName,
|
|
874
|
+
category: "solar",
|
|
875
|
+
extended: true,
|
|
876
|
+
observance: { kind: "solar-ingress", rashi, punyaKala: "after-moment-to-sunset" },
|
|
877
|
+
}));
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* One-off regional festivals & jayantis on the HSNA calendar. Most are plain
|
|
881
|
+
* udaya-tithi observances (the tithi present at sunrise); the full-moon
|
|
882
|
+
* jayantis use moonrise-vyāpti; Lohri and the Navrātri Pāraṇā are offsets from
|
|
883
|
+
* another festival. Validated against HSNA 2026 (test/hsna-oneoff.test.ts).
|
|
884
|
+
*/
|
|
885
|
+
export function oneOffFestivalRules(year) {
|
|
886
|
+
// udaya-tithi at sunrise by default; some festivals key on a later kāla (the
|
|
887
|
+
// tithi prevailing at that ritual window), which is given explicitly.
|
|
888
|
+
const T = (id, displayName, month, paksha, tithi, window = "sunrise", precedence = "udaya") => ({
|
|
889
|
+
id,
|
|
890
|
+
displayName,
|
|
891
|
+
month: { purnimanta: month },
|
|
892
|
+
category: "lunar-tithi",
|
|
893
|
+
extended: true,
|
|
894
|
+
observance: { kind: "tithi-pervades", paksha, tithi, window, precedence },
|
|
895
|
+
});
|
|
896
|
+
// full-moon (moonrise-vyāpti)
|
|
897
|
+
const P = (id, displayName, month) => ({
|
|
898
|
+
id,
|
|
899
|
+
displayName,
|
|
900
|
+
month: { purnimanta: month },
|
|
901
|
+
category: "moonrise",
|
|
902
|
+
extended: true,
|
|
903
|
+
observance: { kind: "moonrise", paksha: "shukla", tithi: "purnima" },
|
|
904
|
+
});
|
|
905
|
+
// offset from another rule (`month` omitted — derived rules take their date,
|
|
906
|
+
// and thus their month label, from the referenced festival).
|
|
907
|
+
const D = (id, displayName, from, offsetDays) => ({
|
|
908
|
+
id,
|
|
909
|
+
displayName,
|
|
910
|
+
category: "derived",
|
|
911
|
+
extended: true,
|
|
912
|
+
observance: { kind: "derived", from, offsetDays },
|
|
913
|
+
});
|
|
914
|
+
void year;
|
|
915
|
+
return [
|
|
916
|
+
D("lohri", "Lohri", "makar-sankranti", -1),
|
|
917
|
+
T("phulera-dooj", "Phulera Dhooj", "Phalguna", "shukla", 2),
|
|
918
|
+
T("ugadi-gudi-padwa", "Chaitra Navratri / Ugadi / Gudi Padwa", "Chaitra", "shukla", 1),
|
|
919
|
+
D("chaitra-navratri-parana", "Chaitra Navratri Parana", "rama-navami", 1),
|
|
920
|
+
P("koorm-jayanti", "Koorm Jayanti", "Vaishakha"),
|
|
921
|
+
T("narad-jayanti", "Narad Jayanti", "Jyeshtha", "krishna", 1),
|
|
922
|
+
// Ganga Dussehra — Jyeṣṭha Śukla Daśamī, observed in the ADHIKA Jyeṣṭha when
|
|
923
|
+
// the year has one (Drik 2026: 25 May, Adhika Jyeṣṭha) and in the nija
|
|
924
|
+
// Jyeṣṭha otherwise. `adhika:"prefer-adhika"` encodes exactly that — it
|
|
925
|
+
// resolves every year without hardcoding the leap month.
|
|
926
|
+
{
|
|
927
|
+
id: "ganga-dussehra",
|
|
928
|
+
displayName: "Ganga Dussehra",
|
|
929
|
+
month: { purnimanta: "Jyeshtha" },
|
|
930
|
+
category: "lunar-tithi",
|
|
931
|
+
extended: true,
|
|
932
|
+
observance: {
|
|
933
|
+
kind: "tithi-pervades", paksha: "shukla", tithi: 10,
|
|
934
|
+
window: "sunrise", precedence: "udaya", adhika: "prefer-adhika",
|
|
935
|
+
},
|
|
936
|
+
},
|
|
937
|
+
T("jagannath-rath-yatra", "Jagannath Rath Yatra", "Ashadha", "shukla", 2),
|
|
938
|
+
T("hariyali-teej", "Hariyali Teej", "Shravana", "shukla", 3),
|
|
939
|
+
T("nag-panchami", "Nag Panchami", "Shravana", "shukla", 5),
|
|
940
|
+
// Kajari Teej — evening (candra) worship → pradoṣa-vyāpinī Tṛtīyā.
|
|
941
|
+
T("kajari-teej", "Kajari Teej", "Bhadrapada", "krishna", 3, "pradosha", "max-window-fraction"),
|
|
942
|
+
T("balram-jayanti", "Balram Jayanti", "Bhadrapada", "krishna", 6),
|
|
943
|
+
T("hartalika-teej", "Hartalika Teej", "Bhadrapada", "shukla", 3),
|
|
944
|
+
// Rishi Panchami — canonically madhyāhna-vyāpinī (midday) Pañcamī.
|
|
945
|
+
T("rishi-panchami", "Rishi Panchami", "Bhadrapada", "shukla", 5, "madhyahna", "max-window-fraction"),
|
|
946
|
+
T("anant-chaturdashi", "Anant Chaturdashi", "Bhadrapada", "shukla", 14),
|
|
947
|
+
P("pitru-paksha-begins", "Pitru Paksha Begins (Bhadrapada Purnima)", "Bhadrapada"),
|
|
948
|
+
T("kalparambha", "Kalparambha", "Ashwina", "shukla", 6),
|
|
949
|
+
T("navpatrika-puja", "Navpatrika Puja", "Ashwina", "shukla", 7),
|
|
950
|
+
// Ahoi Ashtami — fast broken at evening star-sight → pradoṣa-vyāpinī Aṣṭamī.
|
|
951
|
+
T("ahoi-ashtami", "Ahoi Ashtami", "Kartika", "krishna", 8, "pradosha", "max-window-fraction"),
|
|
952
|
+
T("kansh-vadh", "Kansh Vadh", "Kartika", "shukla", 10),
|
|
953
|
+
T("tulsi-vivah", "Tulsi Vivah", "Kartika", "shukla", 12),
|
|
954
|
+
P("dattatreya-jayanti", "Dattatreya Jayanti", "Margashirsha"),
|
|
955
|
+
];
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Additional regional festivals & jayantis from the Drik Panchang Calgary
|
|
959
|
+
* calendar, beyond the §4 core and the HSNA one-offs. Each is anchored on its
|
|
960
|
+
* ritual kāla (deity-birth festivals on madhyāhna/pradoṣa/niśīta, snāna rites at
|
|
961
|
+
* sunrise) and verified against Drik Panchang Calgary 2026
|
|
962
|
+
* (test/conformance-calgary-regional.test.ts).
|
|
963
|
+
*
|
|
964
|
+
* Helpers mirror oneOffFestivalRules: T (tithi-pervades), P (moonrise pūrṇimā),
|
|
965
|
+
* A (kṛṣṇa amāvāsyā at sunrise), SI (solar ingress / saṅkrānti-day festival).
|
|
966
|
+
*/
|
|
967
|
+
export function regionalFestivalRules(year) {
|
|
968
|
+
const T = (id, displayName, month, paksha, tithi, window = "sunrise", precedence = "udaya") => ({
|
|
969
|
+
id, displayName, month: { purnimanta: month }, category: "lunar-tithi", extended: true,
|
|
970
|
+
observance: { kind: "tithi-pervades", paksha, tithi, window, precedence },
|
|
971
|
+
});
|
|
972
|
+
const P = (id, displayName, month) => ({
|
|
973
|
+
id, displayName, month: { purnimanta: month }, category: "moonrise", extended: true,
|
|
974
|
+
observance: { kind: "moonrise", paksha: "shukla", tithi: "purnima" },
|
|
975
|
+
});
|
|
976
|
+
const A = (id, displayName, month) => ({
|
|
977
|
+
id, displayName, month: { purnimanta: month }, category: "lunar-tithi", extended: true,
|
|
978
|
+
observance: { kind: "tithi-pervades", paksha: "krishna", tithi: "amavasya", window: "sunrise", precedence: "max-window-fraction" },
|
|
979
|
+
});
|
|
980
|
+
const SI = (id, displayName, rashi) => ({
|
|
981
|
+
id, displayName, category: "solar", extended: true,
|
|
982
|
+
observance: { kind: "solar-ingress", rashi, punyaKala: "after-moment-to-sunset" },
|
|
983
|
+
});
|
|
984
|
+
void year;
|
|
985
|
+
return [
|
|
986
|
+
// ── Māgha ──
|
|
987
|
+
T("ratha-saptami", "Ratha Saptami", "Magha", "shukla", 7),
|
|
988
|
+
T("bhishma-ashtami", "Bhishma Ashtami", "Magha", "shukla", 8, "madhyahna", "max-window-fraction"),
|
|
989
|
+
// ── Chaitra ──
|
|
990
|
+
T("gangaur", "Gangaur / Gauri Puja", "Chaitra", "shukla", 3),
|
|
991
|
+
T("yamuna-chhath", "Yamuna Chhath", "Chaitra", "shukla", 6),
|
|
992
|
+
T("swaminarayan-jayanti", "Swaminarayan Jayanti", "Chaitra", "shukla", 9),
|
|
993
|
+
// ── Vaiśākha — deity-birth jayantis on their ritual kāla ──
|
|
994
|
+
T("parashurama-jayanti", "Parashurama Jayanti", "Vaishakha", "shukla", 3, "pradosha", "max-window-fraction"),
|
|
995
|
+
T("ganga-saptami", "Ganga Saptami", "Vaishakha", "shukla", 7, "madhyahna", "max-window-fraction"),
|
|
996
|
+
T("sita-navami", "Sita Navami", "Vaishakha", "shukla", 9, "madhyahna", "max-window-fraction"),
|
|
997
|
+
T("narasimha-jayanti", "Narasimha Jayanti", "Vaishakha", "shukla", 14, "pradosha", "max-window-fraction"),
|
|
998
|
+
// ── Jyeṣṭha ──
|
|
999
|
+
A("vat-savitri-vrat", "Vat Savitri Vrat", "Jyeshtha"),
|
|
1000
|
+
A("shani-jayanti", "Shani Jayanti", "Jyeshtha"),
|
|
1001
|
+
P("vat-purnima-vrat", "Vat Purnima Vrat", "Jyeshtha"),
|
|
1002
|
+
// ── Bhādrapada ──
|
|
1003
|
+
T("radha-ashtami", "Radha Ashtami", "Bhadrapada", "shukla", 8),
|
|
1004
|
+
T("ganesh-visarjan", "Ganesh Visarjan", "Bhadrapada", "shukla", 14),
|
|
1005
|
+
// ── Āśvina ──
|
|
1006
|
+
SI("vishwakarma-puja", "Vishwakarma Puja", 5), // on Kanya Sankranti day
|
|
1007
|
+
// ── Kārtika ──
|
|
1008
|
+
T("govatsa-dwadashi", "Govatsa Dwadashi", "Kartika", "krishna", 12),
|
|
1009
|
+
T("kali-chaudas", "Kali Chaudas", "Kartika", "krishna", 14, "nishita", "max-window-fraction"),
|
|
1010
|
+
// ── Mārgaśīrṣa ──
|
|
1011
|
+
T("kalabhairav-jayanti", "Kalabhairav Jayanti", "Margashirsha", "krishna", 8, "nishita", "max-window-fraction"),
|
|
1012
|
+
T("vivah-panchami", "Vivah Panchami", "Margashirsha", "shukla", 5),
|
|
1013
|
+
// ── Nakṣatra- and weekday-anchored ──
|
|
1014
|
+
// Onam — Śravaṇa (Thiruvoṇam) nakṣatra at sunrise while the Sun is in Siṃha.
|
|
1015
|
+
{
|
|
1016
|
+
id: "onam", displayName: "Onam (Thiruvonam)", category: "nakshatra", extended: true,
|
|
1017
|
+
observance: { kind: "nakshatra-pervades", nakshatra: "Shravana", solarRashi: 4 },
|
|
1018
|
+
},
|
|
1019
|
+
// Varalakṣmī Vrat — the Friday (weekday 5) before Śrāvaṇa Pūrṇimā.
|
|
1020
|
+
{
|
|
1021
|
+
id: "varalakshmi-vrat", displayName: "Varalakshmi Vrat", category: "derived", extended: true,
|
|
1022
|
+
observance: { kind: "weekday-relative", from: "purnima-snana-shravana", weekday: 5 },
|
|
1023
|
+
},
|
|
1024
|
+
];
|
|
1025
|
+
}
|
|
1026
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1027
|
+
// Public API — allRules(year)
|
|
1028
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1029
|
+
/**
|
|
1030
|
+
* Assemble the complete rule set for `year`: core (§4) + extended (§4b).
|
|
1031
|
+
*
|
|
1032
|
+
* The extended generators are year-aware so that Adhika-month entries can in
|
|
1033
|
+
* future be emitted conditionally; for now they always include the Adhika
|
|
1034
|
+
* Jyeshtha entries (the evaluator handles gracefully for non-adhika years).
|
|
1035
|
+
*
|
|
1036
|
+
* Note: `gita-jayanti` appears in both CORE_RULES and
|
|
1037
|
+
* `ekadashiRules` (as `ekadashi-margashirsha-shukla`). These are separate
|
|
1038
|
+
* rules with different IDs that produce the same date — gita-jayanti as a
|
|
1039
|
+
* named festival and the Margashirsha Shukla Ekadashi as part of the full
|
|
1040
|
+
* Ekadashi calendar. No deduplication is applied; both are surfaced.
|
|
1041
|
+
*/
|
|
1042
|
+
export function allRules(year) {
|
|
1043
|
+
return [
|
|
1044
|
+
...CORE_RULES,
|
|
1045
|
+
...ekadashiRules(year),
|
|
1046
|
+
...sankashtiRules(year),
|
|
1047
|
+
...pradoshRules(year),
|
|
1048
|
+
...masikShivaratriRules(year),
|
|
1049
|
+
...purnimaVratRules(year),
|
|
1050
|
+
...purnimaSnanaRules(year),
|
|
1051
|
+
...amavasyaRules(year),
|
|
1052
|
+
...sankrantiRules(year),
|
|
1053
|
+
...oneOffFestivalRules(year),
|
|
1054
|
+
...regionalFestivalRules(year),
|
|
1055
|
+
CHHATH_RULE,
|
|
1056
|
+
];
|
|
1057
|
+
}
|
|
1058
|
+
//# sourceMappingURL=rules.js.map
|