Skip to content
Documentation · all sections

Types & Exports

panchang · v5.4.0 · MIT

The main result shapes, how you name them in TypeScript and in Go, and the full list of what the package exports.

Everything the library exports is public and stays stable for the life of a major version. Anything it does not export is internal and can change in any release. This page shows the shapes you hold most often, how to name them in TypeScript and in Go, and the full export list.

What a result looks like

getDailyPanchang gives you a DailyPanchangResult. The whole day hangs off it: the five angas under angas, sunrise and sunset under sun, the resolved offset under timezone, the lunar and solar calendars under calendar, and the bad windows under inauspicious. Go splits the library across two packages you import together: panchang holds the engine, and types holds every shape the engine takes and returns. The same value there is a types.DailyPanchangResult, with the same field names capitalised.

import { getDailyPanchang, type DailyPanchangResult } from 'panchang-ts';

const pune = { latitude: 18.52, longitude: 73.86 };
const day: DailyPanchangResult | null =
  getDailyPanchang(new Date('2026-01-15T00:00:00Z'), pune, { timezone: 330 });

if (day) {
  const tithi = day.angas.tithis[0];
  console.log(tithi.name, tithi.paksha, tithi.number);
  console.log(day.timezone.offsetMinutes, day.angas.vara.englishName);
  console.log(day.sun.riseLocal);
}
// Krishna Dwadashi Krishna 12
// 330 Thursday
// 2026-01-15T07:09:46.622+05:30

Both languages name every nested shape, so a helper can name just the piece it needs. TypeScript exports them from the main entry; in Go they all live in types, down to the leaves — types.DailyAngas, types.DailyTithiInfo and types.VaraInfo. The spellings earlier 5.x releases used — panchang.GeoLocation, panchang.Options, panchang.Error — are aliases of the same types and still compile, so existing code needs no change.

import { getDailyPanchang } from 'panchang-ts';
import type { DailyPanchangResult, DailyAngas, DailyTithiInfo, VaraInfo } from 'panchang-ts';

// Every nested shape is exported, so a helper can name the piece it needs.
function firstTithi(angas: DailyAngas): DailyTithiInfo {
  return angas.tithis[0];
}

const day: DailyPanchangResult | null = getDailyPanchang(
  new Date('2026-01-15T00:00:00Z'),
  { latitude: 18.52, longitude: 73.86 },
  { timezone: 330 },
);

if (day) {
  const vara: VaraInfo = day.angas.vara;
  console.log(firstTithi(day.angas).name, day.angas.nakshatras.length, vara.englishName);
}
// Krishna Dwadashi 2 Thursday

Core shapes

These are the leaves you read off a result. NakshatraInfo, YogaInfo and KaranaInfo share four members with TithiInfo — index, name, completionPercentage and endTime — and two of them add their own on top. Match festivals on key, never on name.

interface GeoLocation { latitude: number; longitude: number; elevation?: number; }
interface TimePeriod  { start: Date; end: Date; }   // + startLocal / endLocal strings

interface TithiInfo {
  index: number;               // 0-29
  name: string;
  paksha: string;              // "Shukla"/"Krishna" (en), "शुक्ल"/"कृष्ण" (hi)
  number: number;              // 1-15 within the paksha
  completionPercentage: number;
  endTime: Date | null;
}
// The other three carry the four shared members and add:
//   NakshatraInfo — pada: number; degreesInNakshatra: number
//   KaranaInfo    — type: 'fixed' | 'movable'
//   YogaInfo      — nothing further
// DailyTithiInfo extends with startTime, isActiveAtSunrise and the
// startTimeLocal / endTimeLocal strings.

interface VaraInfo {
  index: number;       // 0 = Sunday … 6 = Saturday
  name: string;        // localized (e.g. "Raviwara")
  shortName: string;
  englishName: string; // always English
}

interface FestivalInfo {
  key: string;          // stable, language-independent id — match on this
  name: string;         // localized — display only
  type: 'major' | 'minor' | 'ekadashi' | 'smarta_ekadashi' | 'vaishnava_ekadashi'
      | 'pradosha' | 'sankranti' | 'eclipse';
  description?: string;
}

type FestivalRegion =
  | 'all'
  | 'tamil-nadu' | 'kerala' | 'karnataka' | 'andhra-pradesh' | 'telangana'
  | 'west-bengal' | 'odisha' | 'assam' | 'bihar' | 'jharkhand'
  | 'gujarat' | 'maharashtra' | 'goa' | 'rajasthan'
  | 'punjab' | 'haryana' | 'himachal-pradesh' | 'uttarakhand'
  | 'uttar-pradesh' | 'madhya-pradesh'
  | 'nepal';
// Legacy slugs accepted (mapped internally): 'tamil' → 'tamil-nadu',
// 'bengal' → 'west-bengal', 'north-india' → 'all'.

key is a plain string, not a union, so a release can add keys without a type change. 5.4 adds two, both of type major. holika_dahan is the Holika Dahan evening, and holi keeps its key but now names the day after it (Rangwali Holi): Delhi 2026 lists holika_dahan on 3 March and holi on 4 March. Code that read the Purnima evening from holi should match holika_dahan instead. karthigai_deepam is emitted for the regions all and tamil-nadu, where it takes the place of that day's masik_karthigai; every other region still lists masik_karthigai there. See Upgrading 5.3 → 5.4.

Dates that name a day

Most Dates in a result are instants. A few name a calendar day instead, and they follow one of two conventions. Both read as the right day when you format them in the location's zone, so always read them that way. Slicing toISOString() or calling getUTCDate() gets one or the other wrong: a day early for the first kind east of UTC, a day late for the second kind west of it.

ValueWhat the Date is
FestivalDay.date from computeFestivalsForYear, and a day from computeAuspiciousDatesForYearThe local midnight that starts the day. computeFestivalsInRange and computeAuspiciousDatesInRange give the range start's local time of day on each date instead
SankrantiEvent.date, getHinduNewYear, computeEkadashiDatesForYear, convertHinduToGregorianThe UTC midnight that falls within the local day: the date’s own UTC midnight at or east of UTC, the next one west of it
import { computeFestivalsForYear, computeSankrantisForYear } from 'panchang-ts';

// Read a day value as a calendar date in the location's zone.
const dayIn = (d: Date, timeZone: string) =>
  new Intl.DateTimeFormat('en-CA', { timeZone }).format(d);

const delhi = { latitude: 28.6139, longitude: 77.2090 };
const akshaya = computeFestivalsForYear(2020, delhi, { timezone: 330 })
  .find((f) => f.festival.key === 'akshaya_tritiya')!;
console.log(dayIn(akshaya.date, 'Asia/Kolkata'));

const ny = { latitude: 40.7128, longitude: -74.0060 };
const mesha = computeSankrantisForYear(2025, ny, { timezone: 'America/New_York' })
  .find((s) => s.rashi === 0)!;
console.log(dayIn(mesha.date, 'America/New_York'));
// 2020-04-26   the value is 00:00 IST that day
// 2025-04-13   the value is 20:00 EDT that day

Since 5.4, SankrantiEvent.date and the solar-region getHinduNewYear (Tamil Nadu, Kerala, Punjab, West Bengal and Assam) follow the second convention west of UTC as well, and so does Odisha, solar since 5.4. In 5.3 those five were the date's own UTC midnight there, which reads a day early in the zone: the New York Mesha Sankranti above came back as 2025-04-13T00:00Z, 12 April in New York. At UTC and east of it, IST included, nothing changed.

Reference frames

A panchang needs a place. If you do not give one, the library falls back to a reference point, and the result tells you which frame it is in. A location you pass comes back unchanged as practical. Frames arrived in 5.2.0.

type PanchangReference = 'traditional' | 'modern' | 'practical'; // frame a result is IN
type ReferenceMode     = 'traditional' | 'modern';               // frame you may ASK for
interface ResolvedLocation { location: GeoLocation; reference: PanchangReference; }
// resolveLocation(loc?, mode = 'traditional'). A half-filled location throws.
import { resolveLocation, TRADITIONAL_REFERENCE, MODERN_REFERENCE } from 'panchang-ts';

const a = resolveLocation();                                       // no location
const b = resolveLocation(undefined, 'modern');
const c = resolveLocation({ latitude: 18.52, longitude: 73.86 });  // yours

console.log(a.reference, a.location.latitude, a.location.longitude);
console.log(b.reference, b.location.latitude, b.location.longitude);
console.log(c.reference, c.location.latitude, c.location.longitude);
console.log(TRADITIONAL_REFERENCE, MODERN_REFERENCE);
// traditional 23.1765 75.7885
// modern 23.1833 82.5
// practical 18.52 73.86
// { latitude: 23.1765, longitude: 75.7885, elevation: 0 } { latitude: 23.1833, longitude: 82.5, elevation: 0 }

The values match, the shape does not. Go has no ResolvedLocation type: ResolveLocation hands back the location, the frame and an error as three separate values. It also has a single Reference type where TypeScript has two, and the two reference points are functions that return a fresh copy each call.

Eclipses & Bhadra

obscuration and magnitude are two different measurements and only one of them is a fraction. Read the comments before you plot either.

interface EclipseInfo {
  kind: 'solar' | 'lunar';
  subtype: 'partial' | 'total' | 'annular' | 'penumbral';
                            // solar: as seen from the location, so a total or
                            // annular phase wholly below the horizon reads
                            // 'partial'
  start: Date; peak: Date; end: Date;
  visibleFromLocation: boolean;  // body above the horizon at PEAK
  obscuration: number;      // disc AREA covered at peak, [0, 1], horizon or not
  magnitude: number;        // catalogue magnitude — disc DIAMETER covered.
                            // Not [0, 1]: >1 for a total eclipse, negative
                            // for a penumbral lunar one.
  sutakStart: Date | null;  // 12 h before start (solar) / 9 h before umbral
                            // first contact (lunar, so later than 9 h before
                            // start); null for a penumbral lunar, no sutak
  sutakEnd: Date | null;    // last contact (moksha); null likewise
  description: string;      // a solar eclipse peaking below the horizon is
                            // described at the deepest phase seen
}

interface BhadraInfo {
  start: Date; end: Date;
  location: 'earth' | 'heaven' | 'paatal';   // vasa at the window's START;
                                             // 'earth' = malefic for all work
  locationName: string;                      // localized display name
  vasa: BhadraVasaSegment[];                 // one segment per Moon rashi the
                                             // window spans, tiling [start, end]
  isActive: boolean;
}
import { computeEclipsesInRange, getDailyPanchang } from 'panchang-ts';

const pune = { latitude: 18.52, longitude: 73.86 };

const [e] = computeEclipsesInRange(
  new Date('2026-01-01T00:00:00Z'), new Date('2026-12-31T00:00:00Z'), pune);
console.log(e.kind, e.subtype, e.peak.toISOString(), e.visibleFromLocation);

const day = getDailyPanchang(new Date('2026-01-06T00:00:00Z'), pune, { timezone: 330 });
const b = day?.inauspicious.bhadra;   // null on a day no Vishti karana touches
if (b) {
  console.log(b.location, b.locationName, b.isActive, b.startLocal);
}
// lunar total 2026-03-03T11:33:42.765Z false
// earth Earth true 2026-01-05T20:54:17.630+05:30

Go names both. An eclipse’s instants are StartMs, PeakMs and EndMs: epoch milliseconds with an ISOString() method, not a Date. SutakStartMs and SutakEndMs are the same thing behind a pointer, nil for a penumbral lunar eclipse, which carries no sutak. Bhadra hangs off the result as a pointer, and is nil on a day no Vishti karana touches.

visibleFromLocation is about the peak and nothing else. For the partial solar eclipse of 29 March 2025 the Sun at New York is still below the horizon at peak, so it reads false and obscuration is the at-peak 0.7751. Since 5.4 the description is written from the deepest phase actually seen, at sunrise: “Partial solar eclipse: 24% obscuration, visible from location.” To ask whether any phase is visible, call isEclipseVisibleAnyPhase, which returns true for it.

Jyotish

A dasha result carries the whole tree, and currentIndex points at the maha dasha running on the date you asked about. ChandraBalamInfo and TarabalaInfo each give you a machine-readable field and a localized one.

type GrahaName = 'Sun' | 'Moon' | 'Mars' | 'Mercury' | 'Jupiter'
               | 'Venus' | 'Saturn' | 'Rahu' | 'Ketu';

interface GrahaPosition {
  planet: GrahaName;
  siderealLongitude: number;
  rashi: RashiInfo;
  degreeInRashi: number;
  nakshatra: NakshatraInfo;
  isRetrograde: boolean;     // always false for Sun/Moon; always true for Rahu/Ketu
}

type DashaLord = 'Ketu' | 'Venus' | 'Sun' | 'Moon' | 'Mars'
              | 'Rahu' | 'Jupiter' | 'Saturn' | 'Mercury';

interface MahaDasha   { lord: DashaLord; startDate: Date; endDate: Date;
                        years: number; antarDashas: AntarDasha[]; }
// AntarDasha and PratyantarDasha are { lord; startDate; endDate }.
interface VimshottariDashaResult {
  currentMahaDashaLord: DashaLord;
  currentIndex: number;
  mahaDashas: MahaDasha[];
}

interface ChandraBalamInfo {
  house: number;                  // 1 = janma rashi; 12 = rashi before janma
  quality: 'strong' | 'weak';     // Shubha houses = 1,3,6,7,10,11
  englishName: string;            // "Shubha" | "Ashubha"
  name: string;
}

interface TarabalaInfo {
  taraIndex: number;              // 0..8 in 9-tara cycle from janma nakshatra
  englishName: string;            // Janma | Sampat | Vipat | Kshema | Pratyari
                                  // | Sadhaka | Vadha | Mitra | Ati-Mitra
  name: string;
  quality: 'auspicious' | 'inauspicious';
}
import { computeVimshottariDashaFromBirth, computeChandraBalam, computeTarabala } from 'panchang-ts';
import type { VimshottariDashaResult, MahaDasha, DashaLord,
              ChandraBalamInfo, TarabalaInfo } from 'panchang-ts';

const birth = new Date('1995-08-15T10:30:00Z');
const dasha: VimshottariDashaResult =
  computeVimshottariDashaFromBirth(birth, 'lahiri', new Date('2026-01-15T00:00:00Z'));

const current: MahaDasha = dasha.mahaDashas[dasha.currentIndex];
const lord: DashaLord = current.lord;
console.log(lord, current.years, current.startDate.toISOString().slice(0, 10));

const cb: ChandraBalamInfo = computeChandraBalam(2, 7);
const tb: TarabalaInfo = computeTarabala(5, 12);
console.log(cb.house, cb.quality, cb.englishName);
console.log(tb.taraIndex, tb.quality, tb.englishName);
// Sun 6 2025-05-14
// 6 strong Shubha
// 7 auspicious Mitra

In Go the language is a required argument rather than an optional one, and ComputeChandraBalam and ComputeTarabala return an error next to the value.

The first maha dasha and its first antar dasha both start at birth, part-way through. To split that antar dasha into pratyantars, use computeVimshottariPratyantarIn(mahaDasha, antarDasha), new in 5.4. It splits the full antar dasha and drops the pratyantars already over at birth, so the list starts with the one running then. computeVimshottariPratyantar is unchanged: it treats whatever span it is given as a whole antar dasha, so on the birth one it squeezes all nine into what is left. On any other antar dasha the two return the same list.

import { computeVimshottariPratyantar, computeVimshottariPratyantarIn } from 'panchang-ts';

// dasha as above: birth 1995-08-15T10:30Z, lahiri
const maha = dasha.mahaDashas[0];   // Mercury, clipped to start at birth
const antar = maha.antarDashas[0];  // Jupiter, the antar dasha running at birth

const within = computeVimshottariPratyantarIn(maha, antar);
const squeezed = computeVimshottariPratyantar(antar);
console.log(within.length, within[0].lord, within[0].endDate.toISOString());
console.log(squeezed.length, squeezed[0].lord, squeezed[0].endDate.toISOString());
// 1 Rahu 1995-09-04T03:45:56.567Z
// 9 Jupiter 1995-08-18T01:36:07.542Z

Full export list

Grouped by job. Everything here comes from the main entry point.

// Primary entry points
getDailyPanchang, getInstantPanchang

// Location and reference frames (new in 5.2.0)
resolveLocation, referenceLocation
TRADITIONAL_REFERENCE, MODERN_REFERENCE   // Ujjain / the Central Station
IST_TIMEZONE, IST_OFFSET_MINUTES          // 'Asia/Kolkata', 330

// Astronomy
getSunrise, getSunset, getMoonrise, getMoonset
getSiderealSunLongitude, getSiderealMoonLongitude, getAyanamsa
formatInZone

// Inauspicious / Muhurta
computeRahuKalam, computeGulikaKalam, computeYamaganda
computeVarjyam, computeVarjyamWindows, computeGandaMula, computeAnandadiYoga
computePanchakaRahita, computeDoGhati, computeGowriPanchangam
computePanchaka, classifyPanchaka, isPanchakaDosha, findPanchakaOnset
computeAbhijitMuhurta, computeBrahmaMuhurta, computeVijayaMuhurta
computeGodhuliMuhurta, computeNishitaMuhurta, computeAmritKalaWindows
computeMadhyahna, computePratahSandhya, computeSayahnaSandhya

// Eclipses
getUpcomingSolarEclipse, getUpcomingLunarEclipse, getEclipseDuringDay
isEclipseVisibleAnyPhase

// Moon phases (new / quarters / full as precise instants)
computeMoonPhasesInRange, computeMoonPhasesForYear

// Jyotish — planets, dashas, transits
computePlanetaryPositions, GRAHA_ABBR
computeVimshottariDasha, computeVimshottariDashaFromBirth
computeVimshottariPratyantar, computeVimshottariPratyantarIn   // ...In is new in 5.4.0
computeAshtottariDasha, computeYoginiDasha, computeCharaDasha, computeNarayanDasha
computeChandraBalam, computeTarabala, computeSadeSati
ASHTOTTARI_ORDER, ASHTOTTARI_YEARS, YOGINI_ORDER, YOGINI_YEARS, YOGINI_PLANET
CHARA_RASHI_YEARS, SAMA_PADA_RASHIS, VISHAMA_PADA_RASHIS

// Jyotish — chart
computeLagna, computeBhava, computeRashiChart, computeNavamsa, computeDivisionalChart
computeHoraLagna, computeGhatiLagna, computeBhavaLagna, computeSripatiLagna
computeDignity

// Jyotish — strength, yogas, sensitive
computeAspects, computeShadbala, computeBhavaBala, computeAshtakavarga
computeYogas, computeJaiminiKarakas
computeVarshaphala, computeTithiPravesha, computeArudhas, computeUpagrahas, computeArgala
ALL_SAHAM_NAMES

// Jyotish — compatibility, doshas
computeAshtakoot, computePathuPorutham
computeMangalDosha, computeMangalCompatibility, computeKaalSarp, computePitruDosha

// KP / Prashna
computeKpSubLord, computeKpCuspalSubLords, computeKpSignificators
computePrashnaChart

// Muhurta engine
scoreMuhurta, computeAuspiciousDatesInRange, computeAuspiciousDatesForYear
computeVaraTithiYogas, STOCK_MUHURTA_RULES
vivahRule, grihaPraveshRule, namakaranaRule, vidyarambhRule, vahanKharidiRule
annaprashanRule, mundanRule, upanayanamRule, karnavedhaRule
aksharabhyasamRule, seemanthamRule, shopOpeningRule, travelStartRule

// Calendar conversion + yearly listings
convertGregorianToHindu, convertHinduToGregorian
getKaliYugaYear, getHinduNewYear, computeSamvat
computeEkadashiDatesForYear, computeSankrantisForYear
computeFestivalsInRange, computeFestivalsForYear
computeEclipsesInRange, computeEclipsesForYear, getUpcomingEclipses

// Static data tables — build one, cache the JSON, then read it back through
// the engine-free subpath entries. No table ships with the package.
buildFestivalsTable, buildEclipsesTable, buildMoonPhasesTable, buildMuhurtaTable

// Errors
PanchangError

Go covers the same ground under Go names, split across two call shapes. Anything that evaluates the ephemeris is a method on a session you create once. Everything else is a package-level function. The shapes those calls take and return come from types, and the enums carry package-level All helpers — AllGrahas, AllDashaLords, AllFestivalRegions — returning every member for an exhaustiveness check.

5.4 adds panchang.ComputeVimshottariPratyantarIn, the Go twin of the one new TypeScript export, and fills in some names Go lacked: the table readers (ReadFestivalsForYear and the rest, below), ComputeJaimini8Karakas, Session.ComputeSripatiLagnaWithCusps, Session.GetUpcomingEclipsesContext and seven more All helpers. Nothing is removed or retyped.

import { getSunrise, getAyanamsa, IST_TIMEZONE, IST_OFFSET_MINUTES } from 'panchang-ts';

const pune = { latitude: 18.52, longitude: 73.86 };
const at = new Date('2026-01-15T00:00:00Z');

console.log(getSunrise(at, pune).toISOString());
console.log(getAyanamsa(at, 'lahiri').toFixed(4));
console.log(IST_TIMEZONE, IST_OFFSET_MINUTES);
// 2026-01-15T01:39:46.622Z
// 24.2276
// Asia/Kolkata 330

Subpath entry points

Build a table once, cache the JSON, then read it back through a subpath entry that pulls no astronomy code into your bundle. No table ships with the package, so you build the one you need.

Build
import { buildFestivalsTable } from 'panchang-ts';

const table = buildFestivalsTable({
  location: { latitude: 18.52, longitude: 73.86 },
  timezoneOffsetMinutes: 330,
  startYear: 2026,
  endYear: 2026,
});
Read back
import { readFestivalsForDate } from 'panchang-ts/festivals';

for (const f of readFestivalsForDate(table, '2026-03-03')) {
  console.log(f.key, f.name, f.type);
}
// holika_dahan Holika Dahan major
// phagli Phagli minor
// (holi, Rangwali Holi, is the next day: 2026-03-04)
Engine-free readers
import { readFestivalsForYear, readFestivalsForDate, readFestivalsYearRange }
  from 'panchang-ts/festivals';
import { readEclipsesForYear, readEclipsesForDate, readEclipsesYearRange }
  from 'panchang-ts/eclipses';
import { readMoonPhasesForYear, readMoonPhasesForDate, readMoonPhasesYearRange }
  from 'panchang-ts/moon-phases';
import { readMuhurtaForYear, readMuhurtaForDate, readMuhurtaYearRange,
         readMuhurtaOccasion, readBestMuhurtaDays }
  from 'panchang-ts/muhurta';

Type the persisted JSON with the file shapes: FestivalsFile, EclipsesFile, MoonPhasesFile and MuhurtaFile, plus the per-day shapes FestivalTableDay, EclipseTableDay, MoonPhaseTableDay and MuhurtaTableDay. All of them come from the main entry. The first three also export a per-entry shape. The muhurta table has none, because a day carries MuhurtaFactor[] instead.

Go gained the same readers in 5.4, as package-level functions in panchang: ReadFestivalsForYear, ReadFestivalsForDate, ReadFestivalsForDateKey and ReadFestivalsYearRange, the same four for eclipses and moon phases, and ReadMuhurtaForYear, ReadMuhurtaForDate, ReadMuhurtaForDateKey, ReadMuhurtaYearRange, ReadMuhurtaOccasion and ReadBestMuhurtaDays. ForDate takes a time.Time and ForDateKey a YYYY-MM-DD string. What they return is typed in types: FestivalTableDay, FestivalTableEntry and the rest.