- Dharmagya
- panchang docs
- Festivals
Festivals
panchang · v5.4.0 · MIT
80+ festivals with stable keys, Smarta/Vaishnava Ekadashi handling, regional scoping across 20 states + Nepal, and the cacheable pre-computed festivals table.
Every daily result carries the festivals for that day. About 80 are covered:
- Ekadashi: 26 variants, with the Smarta and Vaishnava split.
- Sankranti and its regional forms: Pongal, Vishu, Baisakhi, Pohela Boishakh, Bihu, Uttarayan, Lohri.
- Classical festivals dated by canonical times: Janmashtami, Shivaratri, Ganesh Chaturthi, Diwali, Holika Dahan (with Holi the next day), Raksha Bandhan, Karva Chauth, Akshaya Tritiya.
- Regional observances: Gudi Padwa, Gangaur, Teej, Onam, Chhath, Karthigai Deepam.
- Recurring days: Pradosha, Masik Shivaratri, Masik Karthigai, Pushya days, Shravan Somvar.
Reading festivals off a daily result
getDailyPanchang puts the day's festivals in festivals. Each entry has a stable key, a localized name, a type and an optional description.
import { getDailyPanchang } from 'panchang-ts';
const loc = { latitude: 25.3176, longitude: 82.9739 }; // Varanasi
const r = getDailyPanchang(new Date('2026-11-08T12:00:00Z'), loc, { timezone: 330 })!;
for (const f of r.festivals) {
// key: stable id. type: major | minor | ekadashi | smarta_ekadashi
// | vaishnava_ekadashi | pradosha | sankranti | eclipse
console.log(f.key, f.name, f.type, f.description);
}
// narak_chaturdashi Narak Chaturdashi major Purnimanta: Kartika Krishna Paksha
// diwali Diwali major Purnimanta: Kartika Krishna Paksha
// name is localized, so match on key.
console.log(r.festivals.some(f => f.key === 'diwali')); // truepackage main
import (
"fmt"
"time"
"github.com/ishankgupta95/panchang/source/go/v5/panchang"
"github.com/ishankgupta95/panchang/source/go/v5/types"
)
func main() {
s := panchang.New()
loc := types.GeoLocation{Latitude: 25.3176, Longitude: 82.9739} // Varanasi
date := time.Date(2026, 11, 8, 12, 0, 0, 0, time.UTC)
r, ok, err := s.GetDailyPanchang(date, loc, types.PanchangOptions{
Timezone: panchang.OffsetMinutes(330),
})
if err != nil {
panic(err)
}
if !ok {
return // no sunrise that day
}
for _, f := range r.Festivals {
// Key: stable id. Type: major | minor | ekadashi | smarta_ekadashi
// | vaishnava_ekadashi | pradosha | sankranti | eclipse
fmt.Println(f.Key, f.Name, f.Type, f.Description)
}
// narak_chaturdashi Narak Chaturdashi major Purnimanta: Kartika Krishna Paksha
// diwali Diwali major Purnimanta: Kartika Krishna Paksha
hasDiwali := false
for _, f := range r.Festivals {
if f.Key == "diwali" { // Name is localized; match on Key
hasDiwali = true
}
}
fmt.Println(hasDiwali) // true
}Smarta and Vaishnava Ekadashi
On most Ekadashis both fasts fall on the same day, so you get smarta_ekadashi, vaishnava_ekadashi and the generic ekadashi together. When the reference almanac prints the fast on two days, the earlier day is the Smarta fast and the later one the Vaishnava fast. Five geometries split this way:
- Dashami-viddha: the tithi begins between arunodaya and sunrise.
- Vriddha (Pakshavardhini) Dwadashi: the Dwadashi spans two sunrises.
- Vriddha Ekadashi, kshaya Dwadashi: the Ekadashi spans two sunrises and the Dwadashi touches none. The first Ekadashi day is the Smarta fast and the second the Vaishnava fast. Before 5.4 the festival list missed that Smarta fast, although
computeEkadashiDatesForYearalready gave it. - Kshaya: the tithi touches no sunrise at all.
- Trisprisha: the Ekadashi does touch a sunrise, but the Dwadashi is kshaya. The Smarta fast moves back to the Dashami day and the Vaishnava fast keeps the Ekadashi.
In the last two the Smarta day is Dashami at sunrise, not Ekadashi, so a filter on the sunrise tithi misses that fast. Match on the festival type instead.
import { computeFestivalsInRange, formatInZone } from 'panchang-ts';
const varanasi = { latitude: 25.3176, longitude: 82.9739 };
const show = (a: string, b: string) =>
computeFestivalsInRange(new Date(a), new Date(b), varanasi, { timezone: 330 })
.filter(d => d.festival.type.includes('ekadashi'))
.forEach(d => console.log(formatInZone(d.date, 330).slice(0, 10), d.festival.type));
show('2027-10-25', '2027-10-26'); // Dashami-viddha (Indira Ekadashi)
show('2026-08-23', '2026-08-24'); // vriddha Dwadashi (Shravana Putrada)
show('2030-03-15', '2030-03-16'); // vriddha Ekadashi, kshaya Dwadashi (Amalaki)
show('2027-07-29', '2027-07-30'); // kshaya (Yogini)
show('2026-07-10', '2026-07-11'); // trisprisha (Apara Ekadashi)
// 2027-10-25 smarta_ekadashi / 2027-10-25 ekadashi / 2027-10-26 vaishnava_ekadashi
// 2026-08-23 smarta_ekadashi / 2026-08-23 ekadashi / 2026-08-24 vaishnava_ekadashi
// 2030-03-15 smarta_ekadashi / 2030-03-15 ekadashi / 2030-03-16 vaishnava_ekadashi
// 2027-07-29 smarta_ekadashi / 2027-07-29 ekadashi / 2027-07-30 vaishnava_ekadashi
// 2026-07-10 smarta_ekadashi / 2026-07-10 ekadashi / 2026-07-11 vaishnava_ekadashis := panchang.New()
varanasi := types.GeoLocation{Latitude: 25.3176, Longitude: 82.9739}
show := func(y, m, d1, d2 int) {
days, _ := s.ComputeFestivalsInRange(
time.Date(y, time.Month(m), d1, 0, 0, 0, 0, time.UTC),
time.Date(y, time.Month(m), d2, 0, 0, 0, 0, time.UTC),
varanasi, types.YearlyListingOptions{Timezone: panchang.OffsetMinutes(330)})
for _, d := range days {
if strings.Contains(string(d.Festival.Type), "ekadashi") {
fmt.Println(panchang.FormatInZone(time.UnixMilli(d.Date.Ms()), 330)[:10], d.Festival.Type)
}
}
}
show(2027, 10, 25, 26) // Dashami-viddha (Indira Ekadashi)
show(2026, 8, 23, 24) // vriddha Dwadashi (Shravana Putrada)
show(2030, 3, 15, 16) // vriddha Ekadashi, kshaya Dwadashi (Amalaki)
show(2027, 7, 29, 30) // kshaya (Yogini)
show(2026, 7, 10, 11) // trisprisha (Apara Ekadashi)
// 2027-10-25 smarta_ekadashi / 2027-10-25 ekadashi / 2027-10-26 vaishnava_ekadashi
// 2026-08-23 smarta_ekadashi / 2026-08-23 ekadashi / 2026-08-24 vaishnava_ekadashi
// 2030-03-15 smarta_ekadashi / 2030-03-15 ekadashi / 2030-03-16 vaishnava_ekadashi
// 2027-07-29 smarta_ekadashi / 2027-07-29 ekadashi / 2027-07-30 vaishnava_ekadashi
// 2026-07-10 smarta_ekadashi / 2026-07-10 ekadashi / 2026-07-11 vaishnava_ekadashiAny other vriddha Ekadashi, where the tithi is current at two consecutive sunrises and the Dwadashi still reaches one, does not split. Nothing emits on the first day and everything on the second (the reference almanac's Unmilini Mahadwadashi). computeEkadashiDatesForYear lists the Smarta dates only.
Festivals over a range of dates
computeFestivalsInRange(start, end, loc, opts) lists festivals without a daily loop, and computeFestivalsForYear(year, loc, opts) does a whole year. You get one entry per festival, so a day with three festivals gives three entries. Both helpers are documented in Calendar Conversion.
import { computeFestivalsInRange, formatInZone } from 'panchang-ts';
const varanasi = { latitude: 25.3176, longitude: 82.9739 };
const days = computeFestivalsInRange(
new Date('2026-03-01T00:00:00Z'),
new Date('2026-03-31T00:00:00Z'),
varanasi,
{ timezone: 330 },
);
console.log(days.length); // 23 (one entry per festival, not per day)
for (const d of days.filter(d => d.festival.type === 'major')) {
console.log(formatInZone(d.date, 330).slice(0, 10), d.festival.key);
}
// 2026-03-03 holika_dahan
// 2026-03-04 holi
// 2026-03-06 sankashti_chaturthi
// 2026-03-19 ugadi
// 2026-03-19 gudi_padwa
// 2026-03-21 gangaur
// 2026-03-26 rama_navamis := panchang.New()
varanasi := types.GeoLocation{Latitude: 25.3176, Longitude: 82.9739}
days, err := s.ComputeFestivalsInRange(
time.Date(2026, 3, 1, 0, 0, 0, 0, time.UTC),
time.Date(2026, 3, 31, 0, 0, 0, 0, time.UTC),
varanasi,
types.YearlyListingOptions{Timezone: panchang.OffsetMinutes(330)},
)
if err != nil {
panic(err)
}
fmt.Println(len(days)) // 23 (one entry per festival, not per day)
for _, d := range days {
if d.Festival.Type == "major" {
fmt.Println(panchang.FormatInZone(time.UnixMilli(d.Date.Ms()), 330)[:10], d.Festival.Key)
}
}
// 2026-03-03 holika_dahan
// 2026-03-04 holi
// 2026-03-06 sankashti_chaturthi
// 2026-03-19 ugadi
// 2026-03-19 gudi_padwa
// 2026-03-21 gangaur
// 2026-03-26 rama_navamiRead each date in the listing's zone, as formatInZone does here, and never with toISOString(). From computeFestivalsForYear it is the local midnight itself: Akshaya Tritiya 2020 at Delhi comes back as 2020-04-25T18:30:00.000Z, which is 26 April 00:00 IST and which toISOString() reads as the 25th. Since 5.4 that holds across a daylight-saving change too: Diwali 2026 in America/New_York is 2026-11-08T05:00:00.000Z. From computeFestivalsInRange the date carries the range start's local time of day.
Regional scoping
region narrows the regional variants to one Indian state. Pan-Indian festivals emit whatever you pass.
const chennai = { latitude: 13.0827, longitude: 80.2707 };
const amritsar = { latitude: 31.6340, longitude: 74.8723 };
const jan14 = new Date('2025-01-14T12:00:00Z');
const jan13 = new Date('2025-01-13T12:00:00Z');
const names = (r: DailyPanchangResult) => r.festivals.map(f => f.name);
// All regional variants (default):
names(getDailyPanchang(jan14, chennai, { timezone: 330 })!);
// → ["Sankranti","Makar Sankranti","Pongal","Uttarayan","Magh Bihu","Ayyappa Makara Jyothi"]
// Tamil Nadu only:
names(getDailyPanchang(jan14, chennai, { timezone: 330, region: 'tamil-nadu' })!);
// → ["Sankranti","Makar Sankranti","Pongal"]
// Lohri fires on the Hindu day before the Makara transit, in Punjab scope:
getDailyPanchang(jan13, amritsar, { timezone: 330, region: 'punjab' })!
.festivals.some(f => f.key === 'lohri'); // truefunc names(r types.DailyPanchangResult) []string {
out := []string{}
for _, f := range r.Festivals {
out = append(out, f.Name)
}
return out
}
s := panchang.New()
chennai := types.GeoLocation{Latitude: 13.0827, Longitude: 80.2707}
amritsar := types.GeoLocation{Latitude: 31.6340, Longitude: 74.8723}
jan14 := time.Date(2025, 1, 14, 12, 0, 0, 0, time.UTC)
jan13 := time.Date(2025, 1, 13, 12, 0, 0, 0, time.UTC)
ist := panchang.OffsetMinutes(330)
// err/ok checks omitted for brevity.
all, _, _ := s.GetDailyPanchang(jan14, chennai, types.PanchangOptions{Timezone: ist})
fmt.Println(names(all))
// [Sankranti Makar Sankranti Pongal Uttarayan Magh Bihu Ayyappa Makara Jyothi]
tn, _, _ := s.GetDailyPanchang(jan14, chennai, types.PanchangOptions{
Timezone: ist,
InstantPanchangOptions: types.InstantPanchangOptions{Region: types.RegionTamilNadu},
})
fmt.Println(names(tn))
// [Sankranti Makar Sankranti Pongal]
pb, _, _ := s.GetDailyPanchang(jan13, amritsar, types.PanchangOptions{
Timezone: ist,
InstantPanchangOptions: types.InstantPanchangOptions{Region: types.RegionPunjab},
})
lohri := false
for _, f := range pb.Festivals {
if f.Key == "lohri" {
lohri = true
}
}
fmt.Println(lohri) // trueFestivalRegion covers 20 Indian states plus 'nepal' and 'all' (the default). The older slugs 'tamil', 'bengal' and 'north-india' still work. The full union is in Types & Exports.
karthigai_deepam is the one key that displaces another by region. It emits for 'all' and 'tamil-nadu' only, and on its day it takes the place of masik_karthigai. Every other region still lists masik_karthigai on that day: at Chennai on 4 December 2025, 'tamil-nadu' gives karthigai_deepam and 'kerala' gives masik_karthigai. A table built for 'all' and filtered by region afterwards therefore loses that month's Masik Karthigai outside Tamil Nadu, so build the table for the region you serve.
Pre-computed table
You can get festival dates without running the engine in your app. Compute a table once with buildFestivalsTable, cache the JSON, and read it back through the engine-free panchang-ts/festivals entry point.
import { buildFestivalsTable } from 'panchang-ts'; // uses the engine
// Build at your build time, or on first launch in the background.
const table = buildFestivalsTable({
location: { latitude: 25.3176, longitude: 82.9739 }, // Varanasi
timezoneOffsetMinutes: 330, // IST; -300 = US Eastern, 0 = UK
startYear: 2024,
endYear: 2031,
languages: ['en', 'hi'], // drop 'hi' for ~15% less, not half
referenceLocation: 'Varanasi',
});
// …persist `table` as JSON (disk / MMKV / your bundler's asset pipeline).s := panchang.New()
opts := types.BuildFestivalsTableOptions{
Location: types.GeoLocation{Latitude: 25.3176, Longitude: 82.9739},
TimezoneOffsetMinutes: 330,
StartYear: 2024,
EndYear: 2031,
Languages: []types.FestivalsTableLanguage{types.TableLangEn, types.TableLangHi},
ReferenceLocation: "Varanasi",
}
table, err := s.BuildFestivalsTable(opts)
if err != nil {
panic(err)
}
blob, _ := json.Marshal(table)
os.WriteFile("festivals.json", blob, 0o644) // …or MMKV, or your asset pipelineimport {
readFestivalsForYear,
readFestivalsForDate,
readFestivalsYearRange,
} from 'panchang-ts/festivals'; // engine-free
// Reads are lookups — no engine, no ephemeris.
readFestivalsYearRange(table); // { start: 2024, end: 2031 }
readFestivalsForYear(table, 2026)!.length; // 146 festival days
const diwali = readFestivalsForYear(table, 2026)!
.find(d => d.festivals.some(f => f.key === 'diwali'))!.date; // '2026-11-08'
readFestivalsForDate(table, diwali).map(f => f.name);
// → ["Narak Chaturdashi","Diwali"]
readFestivalsForDate(table, diwali, 'hi').map(f => f.name);
// → ["नरक चतुर्दशी","दिवाली"]blob, err := os.ReadFile("festivals.json")
if err != nil {
panic(err)
}
var table types.AnyFestivalsFile // decodes a v1 (4.x) table too
if err := json.Unmarshal(blob, &table); err != nil {
panic(err)
}
// Reads are lookups: no Session, no ephemeris.
fmt.Println(panchang.ReadFestivalsYearRange(table)) // {2024 2031}
days, _ := panchang.ReadFestivalsForYear(table, 2026, types.TableLangEn)
fmt.Println(len(days)) // 146 festival days
diwali := ""
for _, d := range days {
for _, f := range d.Festivals {
if f.Key == "diwali" {
diwali = d.Date // "2026-11-08"
}
}
}
names := func(lang types.FestivalsTableLanguage) (out []string) {
for _, f := range panchang.ReadFestivalsForDateKey(table, diwali, lang) {
out = append(out, f.Name)
}
return out
}
fmt.Printf("%q\n", names(types.TableLangEn)) // ["Narak Chaturdashi" "Diwali"]
fmt.Printf("%q\n", names(types.TableLangHi)) // ["नरक चतुर्दशी" "दिवाली"]panchang-ts/festivals imports no astronomy code, so a client bundle that only reads a table never pulls in the engine. Keep buildFestivalsTable on the build or server side (or behind a one-time on-device warm-up) and ship only the JSON. The eight-year bilingual table above is about 82 KB, and the same table run through 2033 about 95 KB. Tables written by v1 (4.x) still read, with key empty on their entries.
In Go both sides work. BuildFestivalsTableContext is the same build under a context.Context, and stops early when it is cancelled. The readers, new in the 5.4 module, are package functions that need no Session: ReadFestivalsForYear, ReadFestivalsForDate (a time.Time, dated at the table's offset), ReadFestivalsForDateKey (a YYYY-MM-DD string) and ReadFestivalsYearRange. They take a types.AnyFestivalsFile, which decodes a v1 (4.x) table too, filling Raw instead of Dict and Packed. Wrap a table you just built with its AsAny method. A program that decodes a table with types alone links no engine code. Calling the readers imports panchang, which links some engine code with it, though a read computes nothing.
Eclipses are excluded here. Visibility is location-dependent, so they get their own table at panchang-ts/eclipses (see Eclipses & Moon Phases).
Dating notes
Each festival lands on a calendar day by the kala its tradition uses: madhyahna for Ganesh Chaturthi, pradosha for Diwali, nishita for Maha Shivaratri, moonrise for Karva Chauth. Anything without a canonical-time rule uses the tithi at sunrise.
The windows are the reference almanac's. Daytime runs from sunrise to sunset and the night from sunset to the next sunrise. Madhyahna is the third fifth of the daytime and aparahna the fourth. Pradosha is the first fifth of the night, nishita the 8th of its 15 muhurtas, and arunodaya the 96 minutes before sunrise. The day is chosen from the tithi's exact span against those windows, by one of these rules:
| Rule | Festivals | Delhi example |
|---|---|---|
| The day whose window holds more of the tithi. On a tie, or when both windows are full, the earlier day. | Ganesh and Vinayaka Chaturthi (madhyahna); Dhanteras, Diwali and Parashurama Jayanti (pradosha); Govardhan Puja (the whole daytime) | Ganesh Chaturthi 2033: 28 Aug |
| Nishita ladder: a day whose nishita is all Chaturdashi wins, the later of two such days; otherwise the larger overlap. | Maha Shivaratri, Masik Shivaratri | Maha Shivaratri 2018: 13 Feb |
| Aparahna ladder: the first full aparahna, otherwise the larger overlap. The next day then takes over if it holds Dashami and Shravana at sunrise and Shravana in its aparahna, and the chosen day’s aparahna lacks Shravana. | Dussehra | 2019: 8 Oct |
| The last day whose window touches the tithi. | Rama Navami (madhyahna); Bhai Dooj and Vat Savitri Amavasya (aparahna) | Rama Navami 2032: 19 Apr |
| The first day whose arunodaya touches Chaturdashi, else the day it begins. | Narak Chaturdashi | 2021: 4 Nov |
| The first day on which Panchami has begun by two fifths of the daytime. | Vasant Panchami | 2020: 29 Jan |
| The first sunrise day on which the tithi lasts at least 3 muhurtas (3/15 of the daytime) after sunrise, else the day it begins. | Raksha Bandhan, Yajur Upakarma, Nag Panchami, Akshaya Tritiya | Akshaya Tritiya 2020: 26 Apr |
| The first day on which Navami has begun before sunset less two muhurtas. | Maha Navami | 2015: 21 Oct |
| Moonrise; the sunrise day when the tithi touches no moonrise. | Karva Chauth, Sankashti Chaturthi | Sankashti Chaturthi 2025: 10 Oct, by the fallback |
| The tithi at sunrise. When it holds two sunrises, the first. | Most other tithi festivals, among them Ugadi, Gudi Padwa, Navaratri, Durga Ashtami, Anant Chaturdashi, Hanuman Jayanti, and Guru, Sharad and Kartika Purnima | Anant Chaturdashi 2018: 23 Sep |
| The tithi at sunrise. When it holds two sunrises, the second. | Hariyali, Kajari and Hartalika Teej, Gangaur, Jagannath Rath Yatra | Hartalika Teej 2006: 27 Aug |
Krishna Janmashtami keeps its own nishita rule. The full mapping of festivals to kalas is in Accuracy. Karva Chauth, Dhanteras, Narak Chaturdashi and Diwali emit with Purnimanta paksha naming.
A kshaya tithi begins and ends between two sunrises, so it is current at no sunrise at all. A rule keyed on the sunrise tithi then matched on no day of the year and the festival dropped out of that year entirely. The Hindu day that wholly contains the tithi now claims it, which is what the reference almanac publishes.
Only phagli still opts out of containment (see the deferred rules below). Holi, Narak Chaturdashi and Chhath Usha Arghya opted out before 5.4 and now have anchors of their own. With these rules every once-a-year festival except Phagli is listed exactly once a year at Delhi from 2000 to 2035.
Pradosh vrat
pradosha goes to the day whose pradosha window overlaps Trayodashi most, the earlier day winning a tie or when both windows are full. Its description names the weekday form. At Delhi that gives 24 dates in 2025, among them Ravi Pradosha on 9 February, and 25 in 2027, matching the reference almanac's lists for both years.
Holika Dahan and Holi
holika_dahan is the Holika Dahan evening, and holi is the day after it, Rangwali Holi, which is how the reference almanac publishes them. At Delhi in 2026, holika_dahan falls on 3 March and holi on 4 March. The evening comes from the Nirnaya Sindhu ladder, applied to the pradosha of the day Purnima begins and of the day after, with Bhadra taken as the first half of Purnima:
- If both pradoshas hold Purnima, the first that holds some of it after Bhadra has ended, and otherwise the first.
- If one does, that evening. It moves to the next evening only when that pradosha holds no Purnima free of Bhadra, Bhadra lasts past the middle of the night, and Purnima still fills at least seven eighths of the next day's daylight (three quarters when the Pratipada that follows is longer than Purnima).
- If neither does, the day Purnima begins, or the next day when it begins after sunset.
Before 5.4 holi was the Purnima sunrise day, and there was no holika_dahan.
Chhath, Maha Navami and Varamahalakshmi
Chhath is anchored on one day. chhath_sandhya_arghya is the Kartika Shukla Shashthi sunrise day, and chhath_nahay_khay, chhath_kharna and chhath_usha_arghya fall two days before, one day before and one day after it. The four always run on consecutive days: 25 to 28 October 2025 at Delhi.
maha_navami takes the first day on which Navami has begun before sunset less two muhurtas, so it can share a day with durga_ashtami: both fall on 21 October 2015 at Delhi. bathukamma_saddula is the Durgashtami day, 30 September 2025. varamahalakshmi is the Friday in the seven days that end on the Shravana Purnima sunrise day: 8 August 2025, and 28 August 2026, when the Purnima day is itself a Friday.
Masik Karthigai and Karthigai Deepam
masik_karthigai falls once per Krittika transit, on the first day whose sunrise or sunset is in Krittika. karthigai_deepam is the Masik Karthigai day of the Tamil month Karthigai nearest the full moon, and replaces masik_karthigai on that day for the regions in Regional scoping. Chennai 2025 has 13 masik_karthigai days and karthigai_deepam on 4 December.
Onam
onam is one day a year, the Thiruvonam of the solar month Chingam. When Thiruvonam holds two Chingam sunrises it takes the first, when it holds none the day that contains it, and when Chingam has two Thiruvonams the later: 15 September 2024 and 16 September 2013 at Kochi. An adhika lunar month no longer suppresses it.
Monday, Tuesday and Saturday vratas
shravan_somvar, mangala_gauri, kartik_somvar and magha_shanivar count their month in your masaSystem. The default, 'purnimanta', gives the North Indian days, 'amanta' the southern ones, and region 'nepal' the solar month. Adhika months still count, and bonalu is not affected.
import { computeFestivalsForYear, formatInZone } from 'panchang-ts';
const delhi = { latitude: 28.6139, longitude: 77.2090 };
const mondays = (opts: { masaSystem?: 'amanta'; region?: 'nepal' } = {}) =>
computeFestivalsForYear(2025, delhi, { timezone: 330, ...opts })
.filter(d => d.festival.key === 'shravan_somvar')
.map(d => formatInZone(d.date, 330).slice(0, 10));
mondays(); // purnimanta, the default
// → ["2025-07-14","2025-07-21","2025-07-28","2025-08-04"]
mondays({ masaSystem: 'amanta' });
// → ["2025-07-28","2025-08-04","2025-08-11","2025-08-18"]
mondays({ region: 'nepal' }); // the solar month
// → ["2025-07-21","2025-07-28","2025-08-04","2025-08-11"]s := panchang.New()
delhi := types.GeoLocation{Latitude: 28.6139, Longitude: 77.2090}
mondays := func(opts types.YearlyListingOptions) []string {
opts.Timezone = panchang.OffsetMinutes(330)
days, _ := s.ComputeFestivalsForYear(2025, delhi, opts)
out := []string{}
for _, d := range days {
if d.Festival.Key == "shravan_somvar" {
out = append(out, panchang.FormatInZone(time.UnixMilli(d.Date.Ms()), 330)[:10])
}
}
return out
}
fmt.Println(mondays(types.YearlyListingOptions{})) // purnimanta, the default
// [2025-07-14 2025-07-21 2025-07-28 2025-08-04]
fmt.Println(mondays(types.YearlyListingOptions{MasaSystem: types.Amanta}))
// [2025-07-28 2025-08-04 2025-08-11 2025-08-18]
fmt.Println(mondays(types.YearlyListingOptions{Region: types.RegionNepal})) // the solar month
// [2025-07-21 2025-07-28 2025-08-04 2025-08-11]The purnimanta list is the default, so since 5.4 a caller that never sets masaSystem gets the North Indian dates. Before, all four always counted the amanta month, which is the list 'amanta' still gives.
Rules still waiting on a reference
Three rules are deferred until there is a reference capture to check them against. Until then they behave like this:
- Phagli takes every day whose sunrise holds Phalguna Purnima. Delhi lists it on both 15 and 16 March 2033, and not at all in 2018.
- Pradosh has no fallback. A Trayodashi that touches neither day's pradosha gives that paksha no Pradosh, as between 24 January and 23 February 2013 at Delhi, one of three such gaps there from 2000 to 2035.
- Guru Purnima keeps the plain sunrise rule. A refinement that would take the previous day when Purnima lasts under about one muhurta after sunrise is not applied.
