@ishubhamx/panchangam-js
Version:
Enhanced Indian Panchangam (Hindu Calendar) library with comprehensive Vedic features including Muhurta calculations, planetary positions, Rashi placements, and auspicious/inauspicious time calculations
302 lines • 15.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPanchangam = getPanchangam;
exports.getPanchangamDetails = getPanchangamDetails;
const astronomy_engine_1 = require("astronomy-engine");
const ayanamsa_1 = require("./ayanamsa");
const calculations_1 = require("./calculations");
const festivals_1 = require("./festivals");
const choghadiya_1 = require("./muhurta/choghadiya");
const gowri_1 = require("./muhurta/gowri");
const shoola_1 = require("./shoola");
const chandrashtama_1 = require("./chandrashtama");
const tarabalam_1 = require("./tarabalam");
/**
* Validates inputs for getPanchangam / getPanchangamDetails.
* @throws {Error} If any input is invalid.
*/
function validateInputs(date, observer, options) {
// Date validation
if (!(date instanceof Date) || isNaN(date.getTime())) {
throw new Error('Invalid date: expected a valid Date object.');
}
// Observer validation
if (observer == null || typeof observer !== 'object') {
throw new Error('Invalid observer: expected an Observer object with latitude, longitude, and height.');
}
if (typeof observer.latitude !== 'number' || !isFinite(observer.latitude) ||
observer.latitude < -90 || observer.latitude > 90) {
throw new Error(`Invalid observer latitude: ${observer.latitude}. Must be between -90 and 90.`);
}
if (typeof observer.longitude !== 'number' || !isFinite(observer.longitude) ||
observer.longitude < -180 || observer.longitude > 180) {
throw new Error(`Invalid observer longitude: ${observer.longitude}. Must be between -180 and 180.`);
}
if (typeof observer.height !== 'number' || !isFinite(observer.height) ||
observer.height < -500 || observer.height > 100000) {
throw new Error(`Invalid observer height: ${observer.height}. Must be between -500 and 100000 meters.`);
}
// Options validation
if (options != null) {
if (typeof options !== 'object') {
throw new Error('Invalid options: expected a PanchangamOptions object.');
}
if (options.timezoneOffset != null) {
if (typeof options.timezoneOffset !== 'number' || !isFinite(options.timezoneOffset) ||
options.timezoneOffset < -720 || options.timezoneOffset > 840) {
throw new Error(`Invalid timezoneOffset: ${options.timezoneOffset}. Must be between -720 and 840 minutes.`);
}
}
}
}
function getPanchangam(date, observer, options) {
validateInputs(date, observer, options);
const sunrise = (0, calculations_1.getSunrise)(date, observer, options);
const sunset = (0, calculations_1.getSunset)(date, observer, options);
const moonrise = (0, calculations_1.getMoonrise)(date, observer, options);
const moonset = (0, calculations_1.getMoonset)(date, observer, options);
// Anchor: Use Sunrise for the Panchang Day attributes. Fallback to input date if no sunrise (Polar/Space).
// This ensures Tithi, Nakshatra, Vara, etc. are consistent for the civil day.
const anchorDate = sunrise || date;
const ayanamsa = (0, ayanamsa_1.getAyanamsa)(anchorDate);
// Calculate attributes at Anchor (Sunrise)
const sunVector = (0, astronomy_engine_1.GeoVector)(astronomy_engine_1.Body.Sun, anchorDate, true);
const moonVector = (0, astronomy_engine_1.GeoVector)(astronomy_engine_1.Body.Moon, anchorDate, true);
const sunTrop = (0, astronomy_engine_1.Ecliptic)(sunVector).elon;
const moonTrop = (0, astronomy_engine_1.Ecliptic)(moonVector).elon;
const sunLon = (sunTrop - ayanamsa + 360) % 360;
const moonLon = (moonTrop - ayanamsa + 360) % 360;
const nakshatraStartTime = (0, calculations_1.findNakshatraStart)(anchorDate, ayanamsa);
const nakshatraEndTime = (0, calculations_1.findNakshatraEnd)(anchorDate, ayanamsa);
const tithiStartTime = (0, calculations_1.findTithiStart)(anchorDate);
const tithiEndTime = (0, calculations_1.findTithiEnd)(anchorDate);
const yogaEndTime = (0, calculations_1.findYogaEnd)(anchorDate, ayanamsa);
const rahuKalam = (sunrise && sunset) ? (0, calculations_1.calculateRahuKalam)(sunrise, sunset, (0, calculations_1.getVara)(anchorDate, observer, options?.timezoneOffset)) : null;
// For transitions, search from Sunrise to Next Sunrise
let nextSunrise = null;
if (sunrise) {
const nextDay = new Date(sunrise.getTime());
nextDay.setDate(nextDay.getDate() + 1);
nextSunrise = (0, calculations_1.getSunrise)(nextDay, observer, options);
}
const karanaTransitions = (sunrise && nextSunrise)
? (0, calculations_1.findKaranaTransitions)(sunrise, nextSunrise)
: [];
const tithiTransitions = (sunrise && nextSunrise)
? (0, calculations_1.findTithiTransitions)(sunrise, nextSunrise)
: [];
const nakshatraTransitions = (sunrise && nextSunrise)
? (0, calculations_1.findNakshatraTransitions)(sunrise, nextSunrise, ayanamsa)
: [];
const yogaTransitions = (sunrise && nextSunrise)
? (0, calculations_1.findYogaTransitions)(sunrise, nextSunrise, ayanamsa)
: [];
const abhijitMuhurta = (sunrise && sunset) ? (0, calculations_1.calculateAbhijitMuhurta)(sunrise, sunset) : null;
let prevSunset;
if (sunrise) {
const prevDate = new Date(date);
prevDate.setDate(prevDate.getDate() - 1);
prevSunset = (0, calculations_1.getSunset)(prevDate, observer, options) || undefined;
}
const brahmaMuhurta = sunrise ? (0, calculations_1.calculateBrahmaMuhurta)(sunrise, prevSunset) : null;
const govardhanMuhurta = (sunrise && sunset) ? (0, calculations_1.calculateGovardhanMuhurta)(sunrise, sunset) : null;
// Vara should be based on Anchor Date (Sunrise)
const vara = (0, calculations_1.getVara)(anchorDate, observer, options?.timezoneOffset);
const yamagandaKalam = (sunrise && sunset) ? (0, calculations_1.calculateYamagandaKalam)(sunrise, sunset, vara) : null;
const gulikaKalam = (sunrise && sunset) ? (0, calculations_1.calculateGulikaKalam)(sunrise, sunset, vara) : null;
const durMuhurta = (sunrise && sunset) ? (0, calculations_1.calculateDurMuhurta)(sunrise, sunset, vara) : null;
// Planetary positions at INSTANT (date), not Sunrise
// Ayanamsa change in 1 day is small, so using anchor ayanamsa is acceptable for positions too,
// or calculate instant ayanamsa. Let's reuse anchor for efficiency as difference is negligible.
const rahuPos = (0, calculations_1.getRahuPosition)(date, ayanamsa);
const planetaryPositions = {
sun: (0, calculations_1.getPlanetaryPosition)(astronomy_engine_1.Body.Sun, date, ayanamsa),
moon: (0, calculations_1.getPlanetaryPosition)(astronomy_engine_1.Body.Moon, date, ayanamsa),
mars: (0, calculations_1.getPlanetaryPosition)(astronomy_engine_1.Body.Mars, date, ayanamsa),
mercury: (0, calculations_1.getPlanetaryPosition)(astronomy_engine_1.Body.Mercury, date, ayanamsa),
jupiter: (0, calculations_1.getPlanetaryPosition)(astronomy_engine_1.Body.Jupiter, date, ayanamsa),
venus: (0, calculations_1.getPlanetaryPosition)(astronomy_engine_1.Body.Venus, date, ayanamsa),
saturn: (0, calculations_1.getPlanetaryPosition)(astronomy_engine_1.Body.Saturn, date, ayanamsa),
rahu: rahuPos,
ketu: (0, calculations_1.getKetuPosition)(rahuPos)
};
const chandrabalam = (0, calculations_1.calculateChandraBalam)(moonLon, sunLon);
const currentHora = (0, calculations_1.getCurrentHora)(date, sunrise || date, observer, options?.timezoneOffset);
const tithi = (0, calculations_1.getTithi)(sunLon, moonLon);
const calendarType = options?.calendarType ?? 'purnimanta';
// Masa/Samvat should be based on Anchor (Day's Month/Year)
const masa = (0, calculations_1.getMasa)(sunLon, moonLon, anchorDate, calendarType);
const paksha = (0, calculations_1.getPaksha)(tithi);
const ritu = (0, calculations_1.getRitu)(sunTrop);
const ayana = (0, calculations_1.getAyana)(sunTrop);
const samvat = (0, calculations_1.getSamvat)(anchorDate, masa.index);
// Phase 3: Planetary Details
const nakshatraPada = (0, calculations_1.getNakshatraPada)(moonLon);
const moonRashi = (0, calculations_1.getRashi)(moonLon);
const sunRashi = (0, calculations_1.getRashi)(sunLon);
const sunNakshatra = (0, calculations_1.getSunNakshatra)(sunLon);
const udayaLagna = (0, calculations_1.getUdayaLagna)(sunrise || date, observer, ayanamsa);
const moonRashiTransitions = (sunrise && nextSunrise)
? (0, calculations_1.findRashiTransitions)(sunrise, nextSunrise, ayanamsa)
: [];
// Phase 4: Advanced Muhurta
const nakshatraEnd = nakshatraTransitions.length > 0 ? nakshatraTransitions[0].endTime : nextSunrise; // Approximate if not found today
const currentNakshatraStart = (0, calculations_1.findNakshatraStart)(date, ayanamsa) || date; // Fallback to now if start not found (e.g. earlier than search window)
// Note: findNakshatraStart scans back 25h. If null, it means start is way back.
// We calculate from current time if start not found.
// Varjyam & Amrit Kalam: Check Current, Previous, and Next Nakshatras
const amritKalam = [];
const varjyam = [];
const checkAndAdd = (nakIndex, start, end) => {
if (!start || !end)
return;
const v = (0, calculations_1.calculateVarjyam)(nakIndex, start, end);
varjyam.push(...v);
const a = (0, calculations_1.calculateAmritKalam)(nakIndex, start, end);
if (a)
amritKalam.push(a);
};
// 1. Current Nakshatra
const currentNakIndex = (0, calculations_1.getNakshatra)(moonLon);
const nStart = nakshatraStartTime || date; // Fallback
const nEnd = nakshatraEndTime || nextSunrise || date; // Fallback
checkAndAdd(currentNakIndex, nStart, nEnd);
// 2. Previous Nakshatra (if current started after sunrise/start of day)
// We need the Previous Nakshatra End = Current Nakshatra Start.
if (nakshatraStartTime) { // If start is known and valid
const prevNakIndex = (currentNakIndex - 1 + 27) % 27;
// We need Previous Start.
// We can find it by searching backwards from nakshatraStartTime.
// We search from 1 minute before current start.
const prevSearchDate = new Date(nakshatraStartTime.getTime() - 60000);
const prevStart = (0, calculations_1.findNakshatraStart)(prevSearchDate, ayanamsa);
if (prevStart) {
checkAndAdd(prevNakIndex, prevStart, nakshatraStartTime);
}
}
// 3. Next Nakshatra (if current ends today)
// nakshatraEndTime is the End of Current.
if (nakshatraEndTime) {
const nextNakIndex = (currentNakIndex + 1) % 27;
// Next Start = Current End.
// We search from 1 minute after next start to find its end.
const nextSearchDate = new Date(nakshatraEndTime.getTime() + 60000);
const nextEnd = (0, calculations_1.findNakshatraEnd)(nextSearchDate, ayanamsa);
if (nextEnd) {
checkAndAdd(nextNakIndex, nakshatraEndTime, nextEnd);
}
}
// Filter out periods completely outside "Today"?
// Usually Drik shows periods even if they spill over to next day early morning.
// We return all found relevant to the Nakshatras spanning "Today".
// Sort
const sortByStart = (a, b) => a.start.getTime() - b.start.getTime();
amritKalam.sort(sortByStart);
varjyam.sort(sortByStart);
return {
tithi,
nakshatra: (0, calculations_1.getNakshatra)(moonLon),
yoga: (0, calculations_1.getYoga)(sunLon, moonLon),
karana: (0, calculations_1.getKarana)(sunLon, moonLon),
vara,
ayanamsa,
sunrise,
sunset,
moonrise,
moonset,
nakshatraStartTime,
nakshatraEndTime,
tithiStartTime,
tithiEndTime,
yogaEndTime,
rahuKalamStart: rahuKalam?.start || null,
rahuKalamEnd: rahuKalam?.end || null,
karanaTransitions,
tithiTransitions,
nakshatraTransitions,
yogaTransitions,
moonRashiTransitions,
// Unified List
tithis: tithiTransitions,
nakshatras: nakshatraTransitions,
yogas: yogaTransitions,
karanas: karanaTransitions,
rashis: moonRashiTransitions,
// Enhanced Vedic Features
amritKalam,
varjyam,
// Special Yogas
specialYogas: (0, calculations_1.getSpecialYoga)(vara, currentNakIndex),
// Phase 6: Dasha System
// We calculate Dasha based on the Moon position at the given 'date'.
// This signifies: "If a child were born at this time, what is the Dasha?"
// Or "What is the ruling Dasha for the day?"
vimshottariDasha: (0, calculations_1.calculateVimshottariDasha)(moonLon, anchorDate),
// Phase 7: Festivals (v3.0.0 API with Udaya Tithi)
festivals: (0, festivals_1.getFestivals)({
date,
observer,
sunrise: sunrise || date,
sunset: sunset || undefined,
masa,
paksha,
// tithi from getTithi() is 0-indexed (0-29). Festivals expect 1-indexed (1-30).
tithi: tithi + 1,
nakshatra: currentNakIndex,
vara,
includeSolarFestivals: true,
includeMultiDaySpans: true,
calendarType: calendarType,
timezoneOffset: options?.timezoneOffset
}),
// Phase 8: Advanced Muhurta (v2.1)
choghadiya: (sunrise && sunset && nextSunrise)
? (0, choghadiya_1.calculateChoghadiya)(sunrise, sunset, nextSunrise, vara)
: { day: [], night: [] },
gowri: (sunrise && sunset && nextSunrise)
? (0, gowri_1.calculateGowriPanchangam)(sunrise, sunset, nextSunrise, vara)
: { day: [], night: [] },
abhijitMuhurta,
brahmaMuhurta,
govardhanMuhurta,
yamagandaKalam,
gulikaKalam,
durMuhurta,
planetaryPositions,
chandrabalam,
currentHora,
dishaShoola: (0, shoola_1.getDishaShoola)(vara),
chandrashtama: (options?.birthMoonRashi !== undefined)
? (0, chandrashtama_1.getChandrashtama)(options.birthMoonRashi, moonRashi.index)
: null,
tarabalam: (options?.birthNakshatra !== undefined)
? (0, tarabalam_1.getTarabalam)(options.birthNakshatra, (0, calculations_1.getNakshatra)(moonLon))
: null,
// Phase 3: Planetary Details
nakshatraPada,
moonRashi,
sunRashi,
sunNakshatra,
udayaLagna,
// Phase 2: Calendar Units
masa,
paksha,
ritu,
ayana,
samvat
};
}
function getPanchangamDetails(date, observer, options) {
validateInputs(date, observer, options);
const panchangam = getPanchangam(date, observer, options);
const sunrise = (0, calculations_1.getSunrise)(date, observer, options);
const sunset = (0, calculations_1.getSunset)(date, observer, options);
const nakshatraEndTime = (0, calculations_1.findNakshatraEnd)(date, panchangam.ayanamsa);
return {
...panchangam,
sunrise,
sunset,
nakshatraEndTime,
};
}
//# sourceMappingURL=panchangam.js.map