@@ -11,106 +9,96 @@
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
+ * along with this program. If not, see .
*/
.pragma library
.import "qrc:/resources/PreCalculated_Data.js" as Bsdata
+.import "qrc:/resources/EventData.js" as EventsData
-// --- Caching ---
+// Caching
var calculationCache = {};
function clearCache() {
calculationCache = {};
}
-// --- Surya Siddhanta Constants ---
+// Surya Siddhanta Constants
var YugaRotation = {
- 'star': 1582237828,
- 'sun': 4320000,
- 'moon': 57753336,
- 'mercury': 17937060,
- 'venus': 7022376,
- 'mars': 2296832,
- 'jupiter': 364220,
- 'saturn': 146568,
- 'Candrocca': 488203,
+ 'star': 1582237828, 'sun': 4320000, 'moon': 57753336,
+ 'mercury': 17937060, 'venus': 7022376, 'mars': 2296832,
+ 'jupiter': 364220, 'saturn': 146568, 'Candrocca': 488203,
'Rahu': -232238
};
var YugaCivilDays = 1577917828; // YugaRotation.star - YugaRotation.sun
var KaliEpoch = 588465.5;
var PlanetApogee = { 'sun': 77 + 17 / 60 };
var PlanetCircumm = { 'sun': 13 + 50 / 60, 'moon': 31 + 50 / 60 };
+var rad = 180 / Math.PI;
-
-// --- Panchanga Names ---
+// Panchanga Names
var tithiNamesList = [
- "प्रतिपदा", "द्वितीया", "तृतीया", "चतुर्थी", "पञ्चमी", "षष्ठी", "सप्तमी", "अष्टमी", "नवमी", "दशमी",
- "एकादशी", "द्वादशी", "त्रयोदशी", "चतुर्दशी", "पूर्णिमा", "अमावस्या"
-];
+ "प्रतिपदा", "द्वितीया", "तृतीया", "चतुर्थी", "पञ्चमी", "षष्ठी", "सप्तमी", "अष्टमी", "नवमी", "दशमी",
+ "एकादशी", "द्वादशी", "त्रयोदशी", "चतुर्दशी", "पूर्णिमा", "अमावस्या"
+ ];
var nakshatras = [
- "अश्विनी", "भरणी", "कृत्तिका", "रोहिणी", "मृगशिरा", "आर्द्रा", "पुनर्वसु", "पुष्य", "अश्लेषा",
- "मघा", "पूर्व फाल्गुनी", "उत्तर फाल्गुनी", "हस्त", "चित्रा", "स्वाती", "विशाखा", "अनुराधा",
- "ज्येष्ठा", "मूल", "पूर्वाषाढा", "उत्तराषाढा", "श्रवण", "धनिष्ठा", "शतभिषा", "पूर्व भाद्रपद",
- "उत्तर भाद्रपद", "रेवती"
-];
+ "अश्विनी", "भरणी", "कृत्तिका", "रोहिणी", "मृगशिरा", "आर्द्रा", "पुनर्वसु", "पुष्य", "अश्लेषा",
+ "मघा", "पूर्व फाल्गुनी", "उत्तर फाल्गुनी", "हस्त", "चित्रा", "स्वाती", "विशाखा", "अनुराधा",
+ "ज्येष्ठा", "मूल", "पूर्वाषाढा", "उत्तराषाढा", "श्रवण", "धनिष्ठा", "शतभिषा", "पूर्व भाद्रपद",
+ "उत्तर भाद्रपद", "रेवती"
+ ];
var yogas = [
- "विष्कम्भ", "प्रीति", "आयुष्मान्", "सौभाग्य", "शोभन", "अतिगण्ड", "सुकर्म", "धृति", "शूल", "गण्ड",
- "वृद्धि", "ध्रुव", "व्याघात", "हर्षण", "वज्र", "सिद्धि", "व्यतिपात", "वरीयान्", "परिघ", "शिव",
- "सिद्ध", "साध्य", "शुभ", "शुक्ल", "ब्रह्म", "इन्द्र", "वैधृति"
-];
+ "विष्कम्भ", "प्रीति", "आयुष्मान्", "सौभाग्य", "शोभन", "अतिगण्ड", "सुकर्म", "धृति", "शूल", "गण्ड",
+ "वृद्धि", "ध्रुव", "व्याघात", "हर्षण", "वज्र", "सिद्धि", "व्यतिपात", "वरीयान्", "परिघ", "शिव",
+ "सिद्ध", "साध्य", "शुभ", "शुक्ल", "ब्रह्म", "इन्द्र", "वैधृति"
+ ];
var karanas = [
- "किंस्तुघ्न", "बव", "बालव", "कौलव", "तैतिल", "गर", "वणिज", "विष्टि", "शकुनि", "चतुष्पाद", "नाग"
-];
+ "किंस्तुघ्न", "बव", "बालव", "कौलव", "तैतिल", "गर", "वणिज", "विष्टि", "शकुनि", "चतुष्पाद", "नाग"
+ ];
var rashis = [
- "मेष", "वृषभ", "मिथुन", "कर्क", "सिंह", "कन्या",
- "तुला", "वृश्चिक", "धनु", "मकर", "कुम्भ", "मीन"
-];
+ "मेष", "वृषभ", "मिथुन", "कर्क", "सिंह", "कन्या",
+ "तुला", "वृश्चिक", "धनु", "मकर", "कुम्भ", "मीन"
+ ];
var solarMonths = [
- "वैशाख", "ज्येष्ठ", "आषाढ", "श्रावण", "भाद्रपद", "आश्विन",
- "कार्तिक", "मार्गशीर्ष", "पौष", "माघ", "फाल्गुन", "चैत्र"
-];
+ "वैशाख", "ज्येष्ठ", "आषाढ", "श्रावण", "भाद्रपद", "आश्विन",
+ "कार्तिक", "मार्गशीर्ष", "पौष", "माघ", "फाल्गुन", "चैत्र"
+ ];
var weekdays = ["आइतबार", "सोमबार", "मङ्गलबार", "बुधबार", "बिहीबार", "शुक्रबार", "शनिबार"];
var nepaliGregorianMonths = [
- "जनवरी", "फेब्रुअरी", "मार्च", "अप्रिल", "मे", "जून",
- "जुलाई", "अगस्ट", "सेप्टेम्बर", "अक्टोबर", "नोभेम्बर", "डिसेम्बर"
-];
+ "जनवरी", "फेब्रुअरी", "मार्च", "अप्रिल", "मे", "जून",
+ "जुलाई", "अगस्ट", "सेप्टेम्बर", "अक्टोबर", "नोभेम्बर", "डिसेम्बर"
+ ];
-// --- Helper Functions ---
-function zero360(x) {
- x = x - Math.floor(x / 360) * 360;
- return x < 0 ? x + 360 : x;
-}
-function sinDeg(deg) { return Math.sin(deg * Math.PI / 180); }
-function cosDeg(deg) { return Math.cos(deg * Math.PI / 180); }
-function arcsinDeg(x) { return Math.asin(x) * 180 / Math.PI; }
+// Helper Functions
+function zero360(x) { return x - Math.floor(x / 360) * 360; }
+function sinDeg(deg) { return Math.sin(deg / rad); }
+function cosDeg(deg) { return Math.cos(deg / rad); }
+function arcsinDeg(x) { return Math.asin(x) * rad; }
function toDevanagari(num) {
if (num <= 0) return "";
var devanagariNumerals = ["०", "१", "२", "३", "४", "५", "६", "७", "८", "९"];
- return String(num).split('').map(function(digit) {
+ return String(num).split('').map(function (digit) {
return devanagariNumerals[parseInt(digit, 10)];
}).join('');
}
+function formatMonthDay(month, day) {
+ return (month < 10 ? '0' : '') + month + '/' + (day < 10 ? '0' : '') + day;
+}
+
function toJulianDay(year, month, day) {
- // In QML, month is 0-indexed, so we add 1 for calculations
var m = month + 1;
var y = year;
- if (m <= 2) {
- y--;
- m += 12;
- }
+ if (m <= 2) { y--; m += 12; }
var a = Math.floor(y / 100);
var b = 2 - a + Math.floor(a / 4);
- return Math.floor(365.25 * (y + 4716)) +
- Math.floor(30.6001 * (m + 1)) +
- day + b - 1524.5;
+ return Math.floor(365.25 * (y + 4716)) + Math.floor(30.6001 * (m + 1)) + day + b - 1524.5;
}
function fromJulianDay(jd) {
@@ -134,7 +122,7 @@ function fromJulianDay(jd) {
return new Date(Date.UTC(year, month - 1, day));
}
-// --- Surya Siddhanta Core Calculations (Used as fallback) ---
+// Core Surya Siddhanta Calculations
function meanLongitude(ahar, rotation) {
return zero360(rotation * ahar * 360 / YugaCivilDays);
}
@@ -161,80 +149,56 @@ function getTithi(sunLong, moonLong) {
return zero360(moonLong - sunLong) / 12;
}
-function getSolarDay(ahar) {
- var intAhar = Math.floor(ahar);
- for (var i = 0; i <= 32; i++) {
- var t1 = trueLongitudeSun(intAhar - i);
- var t2 = trueLongitudeSun(intAhar - i + 1);
- if (Math.floor(t1 / 30) !== Math.floor(t2 / 30)) return i + 1;
- }
- return 1;
-}
-
-function getVikramYear(ahar) {
- var kaliYear = Math.floor(ahar * YugaRotation.sun / YugaCivilDays);
- return kaliYear - 3044;
-}
-
-function getBikramSambatInfo(ahar, sunLong) {
- var year = getVikramYear(ahar);
- var monthIndex = Math.floor(sunLong / 30) % 12;
- var day = getSolarDay(ahar);
- return { year: year, monthIndex: monthIndex, day: day, monthName: solarMonths[monthIndex] };
-}
-
function findNewMoon(ahar) {
var getElongation = function(a) { return zero360(trueLongitudeMoon(a) - trueLongitudeSun(a)); };
- var lo = ahar - 2, hi = ahar + 2;
- var el = getElongation(lo), eh = getElongation(hi);
- if (el < eh) el += 360;
- for (var i = 0; i < 30; i++) {
+ var guess = ahar;
+ for (var i = 0; i < 10; i++) {
+ var elong = getElongation(guess);
+ if (elong < 5 || elong > 355) break;
+ var correction = (elong < 180 ? -elong : 360 - elong) / 12.19;
+ guess += correction;
+ }
+ var lo = guess - 2, hi = guess + 2;
+ for (var j = 0; j < 30; j++) {
var mid = (lo + hi) / 2;
var em = getElongation(mid);
- if (el < em) em -= 360;
- if (em > 0) { lo = mid; el = em; }
- else { hi = mid; eh = em; }
+ if (em < 180) { hi = mid; } else { lo = mid; }
}
return (lo + hi) / 2;
}
-function calculateAdhikaMasa(ahar) {
- const tithi = getTithi(trueLongitudeSun(ahar), trueLongitudeMoon(ahar));
- const daysPerTithi = 29.530588 / 30; // ~0.9843 days
- const nextNewMoon = findNewMoon(ahar + (30 - tithi) * daysPerTithi);
- const prevNewMoon = findNewMoon(nextNewMoon - 29.6);
-
- const signAtPrev = Math.floor(trueLongitudeSun(prevNewMoon) / 30);
- const signAtNext = Math.floor(trueLongitudeSun(nextNewMoon) / 30);
-
- if (signAtPrev === signAtNext) {
- return "अधिक " + solarMonths[signAtNext];
+function findPurnima(ahar) {
+ var getElongation = function(a) { return zero360(trueLongitudeMoon(a) - trueLongitudeSun(a)); };
+ var guess = ahar;
+ for (var i = 0; i < 10; i++) {
+ var elong = getElongation(guess);
+ if (Math.abs(elong - 180) < 5) break;
+ var correction = (180 - elong) / 12.19;
+ guess += correction;
}
-
- if ((signAtNext - signAtPrev + 12) % 12 === 2) {
- return "क्षय मास";
+ var lo = guess - 2, hi = guess + 2;
+ for (var j = 0; j < 30; j++) {
+ var mid = (lo + hi) / 2;
+ var em = getElongation(mid);
+ if (em < 180) { lo = mid; } else { hi = mid; }
}
-
- return "छैन";
+ return (lo + hi) / 2;
}
function getSunriseSunset(date, lat, lon, tz) {
- lat = lat || 27.71;
- lon = lon || 85.32;
+ lat = lat || 27.7172;
+ lon = lon || 85.3240;
tz = tz || 5.75;
- var oneDay = 86400000;
- var dayOfYear = Math.ceil((date.getTime() - new Date(date.getUTCFullYear(), 0, 0).getTime()) / oneDay);
-
- var declination = 23.45 * sinDeg(360 / 365 * (dayOfYear - 81));
- var B = (360 / 365) * (dayOfYear - 81);
- var eot = 9.87 * sinDeg(2 * B) - 7.53 * cosDeg(B) - 1.5 * sinDeg(B);
- var timeCorr = (4 * (lon - 15 * tz) + eot) / 60;
- var cosH = (sinDeg(-0.833) - sinDeg(lat) * sinDeg(declination)) / (cosDeg(lat) * cosDeg(declination));
- var H = (cosH >= -1 && cosH <= 1) ? Math.acos(cosH) * 180 / Math.PI : 90;
- var noon = 12 - timeCorr;
- var rise = noon - H / 15;
- var set = noon + H / 15;
-
+ var dayOfYear = Math.floor((date - new Date(date.getUTCFullYear(), 0, 0)) / 86400000);
+ var B = (360 / 365) * (dayOfYear - 81) / rad;
+ var eot = 9.87 * Math.sin(2 * B) - 7.53 * Math.cos(B) - 1.5 * Math.sin(B);
+ var lstm = 15 * tz;
+ var tc = (4 * (lon - lstm) + eot) / 60;
+ var declination = -23.45 * cosDeg(360 / 365 * (dayOfYear + 10));
+ var hourAngleRad = Math.acos((sinDeg(-0.833) - sinDeg(lat) * sinDeg(declination)) / (cosDeg(lat) * cosDeg(declination)));
+ var hourAngle = hourAngleRad * rad;
+ var sunrise = 12 - hourAngle / 15 - tc;
+ var sunset = 12 + hourAngle / 15 - tc;
var formatTime = function(h) {
if (!isFinite(h)) return "N/A";
var hr = Math.floor(h);
@@ -242,60 +206,51 @@ function getSunriseSunset(date, lat, lon, tz) {
if (min === 60) { hr++; min = 0; }
return (hr < 10 ? '0' : '') + hr + ":" + (min < 10 ? '0' : '') + min;
};
- return { sunrise: formatTime(rise), sunset: formatTime(set) };
+ return { sunrise: formatTime(sunrise), sunset: formatTime(sunset) };
}
-// --- Data-Driven Conversion and Info Functions ---
-
function fromBikramSambat(bsYear, monthIndex, day) {
- // Use data if within range, otherwise fallback to astronomical calculation
if (bsYear >= Bsdata.BS_START_YEAR && bsYear <= Bsdata.BS_END_YEAR) {
var daysOffset = 0;
- // Add days for full years between start year and target year
for (var y = Bsdata.BS_START_YEAR; y < bsYear; y++) {
var yearData = Bsdata.NP_MONTHS_DATA[y - Bsdata.BS_START_YEAR];
var totalDaysInYear = 0;
- for(var m = 0; m < 12; m++) {
+ for (var m = 0; m < 12; m++) {
totalDaysInYear += yearData[m];
}
daysOffset += totalDaysInYear;
}
- // Add days for months in the target year
var targetYearData = Bsdata.NP_MONTHS_DATA[bsYear - Bsdata.BS_START_YEAR];
for (let m = 0; m < monthIndex; m++) {
daysOffset += targetYearData[m];
}
- // Add the day of the month
daysOffset += (day - 1);
-
var resultDate = new Date(Bsdata.BS_START_DATE_AD.getTime());
resultDate.setUTCDate(resultDate.getUTCDate() + daysOffset);
return resultDate;
} else {
// Fallback for out-of-range years
- var sakaYear = bsYear - 135;
- var kaliYear = sakaYear + 3179;
- var approxAhar = (kaliYear * YugaCivilDays / YugaRotation.sun);
- approxAhar += monthIndex * 30.5; // Start with an approximation
-
- for (var i = 0; i < 5; i++) { // Iterate to find the start of the solar month
- var sunLong = trueLongitudeSun(approxAhar);
- var targetLong = monthIndex * 30;
- var diff = zero360(targetLong - sunLong);
- if (diff > 180) diff -= 360;
- if (Math.abs(diff) < 0.01) break;
- approxAhar += diff / 0.9856; // Adjust ahar based on Sun's mean motion
+ var YearSaka = bsYear - 135;
+ var YearKali = YearSaka + 3179;
+ var ahar = Math.floor((YearKali * YugaCivilDays) / YugaRotation.sun);
+ var currentDay = getSauraMasaDay(ahar);
+ while (currentDay.m !== monthIndex || currentDay.d !== day) {
+ if (currentDay.m < monthIndex || (currentDay.m === monthIndex && currentDay.d < day)) {
+ ahar += 1;
+ } else {
+ ahar -= 1;
+ }
+ currentDay = getSauraMasaDay(ahar);
}
-
- var targetAhar = approxAhar + (day - 1);
- var jd = targetAhar + KaliEpoch;
- return fromJulianDay(jd);
+ var julian_date = ahar + KaliEpoch;
+ return fromJulianDay(julian_date);
}
}
function getBikramMonthInfo(bsYear, monthIndex) {
if (bsYear >= Bsdata.BS_START_YEAR && bsYear <= Bsdata.BS_END_YEAR) {
var firstDayAd = fromBikramSambat(bsYear, monthIndex, 1);
+ if (!firstDayAd) return null;
var monthData = Bsdata.NP_MONTHS_DATA[bsYear - Bsdata.BS_START_YEAR];
return {
totalDays: monthData[monthIndex],
@@ -320,29 +275,174 @@ function getBikramMonthInfo(bsYear, monthIndex) {
}
}
-function getTodayBsInfo() {
- var now = new Date();
- var jd = toJulianDay(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
- var ahar = jd - KaliEpoch + 0.25 + ((85.3 / 15 - 5.75) / 24);
- var sunLong = trueLongitudeSun(ahar);
- return getBikramSambatInfo(ahar, sunLong);
+function toBikramSambat(gregorianDate) {
+ var isDataAvailable = false;
+ if (Bsdata && Bsdata.BS_START_DATE_AD) {
+ var targetUtcDate = new Date(Date.UTC(gregorianDate.getFullYear(), gregorianDate.getMonth(), gregorianDate.getDate()));
+ var startDate = new Date(Date.UTC(Bsdata.BS_START_DATE_AD.getFullYear(), Bsdata.BS_START_DATE_AD.getMonth(), Bsdata.BS_START_DATE_AD.getDate()));
+ if (targetUtcDate >= startDate && gregorianDate.getFullYear() <= (Bsdata.BS_END_YEAR - 56)) {
+ isDataAvailable = true;
+ }
+ }
+
+ if (isDataAvailable) {
+ var daysOffset = Math.floor((targetUtcDate.getTime() - startDate.getTime()) / 86400000);
+ for (var y = 0; y < Bsdata.NP_MONTHS_DATA.length; y++) {
+ var currentBsYear = Bsdata.BS_START_YEAR + y;
+ var yearData = Bsdata.NP_MONTHS_DATA[y];
+ var daysInYear = 0;
+ for (var m_idx = 0; m_idx < 12; m_idx++) { daysInYear += yearData[m_idx]; }
+ if (daysOffset < daysInYear) {
+ for (var m = 0; m < 12; m++) {
+ var daysInMonth = yearData[m];
+ if (daysOffset < daysInMonth) {
+ return { year: currentBsYear, monthIndex: m, day: daysOffset + 1, monthName: solarMonths[m], isComputed: false };
+ }
+ daysOffset -= daysInMonth;
+ }
+ }
+ daysOffset -= daysInYear;
+ }
+ }
+ // Fallback to astronomical calculation
+ var result = fromGregorianAstronomical(
+ gregorianDate.getUTCFullYear(),
+ gregorianDate.getUTCMonth() + 1,
+ gregorianDate.getUTCDate()
+ );
+ result.isComputed = true;
+ return result;
}
-// --- Shared Name Resolution Utility ---
function resolveTithiName(tithiDay, paksha) {
- if (paksha === "कृष्ण पक्ष" && tithiDay === 15) return tithiNamesList[15]; // Amavasya
- if (paksha === "शुक्ल पक्ष" && tithiDay === 15) return tithiNamesList[14]; // Purnima
+ if (paksha === "कृष्ण पक्ष" && tithiDay === 15) return tithiNamesList[15];
+ if (paksha === "शुक्ल पक्ष" && tithiDay === 15) return tithiNamesList[14];
return tithiNamesList[tithiDay - 1];
}
-// --- Main Calculation Function ---
-function calculate(date, lat = 27.7172, lon = 85.3240, tz = 5.75) {
+function _getPanchangaBasics(date) {
+ var jd = toJulianDay(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
+ var ahar = jd - KaliEpoch + 0.25 + ((85.3240 / 15 - 5.75) / 24);
+ var sunLong = trueLongitudeSun(ahar);
+ var moonLong = trueLongitudeMoon(ahar);
+ var tithiVal = getTithi(sunLong, moonLong);
+ var tithiNum = Math.floor(tithiVal) + 1;
+ var paksha = tithiNum <= 15 ? "शुक्ल पक्ष" : "कृष्ण पक्ष";
+ var tithiDay = tithiNum > 15 ? tithiNum - 15 : tithiNum;
+ var tithiName = resolveTithiName(tithiDay, paksha);
+ var lunarMonthInfo = getLunarMonthNameWithAdhik(ahar);
+ return {
+ ahar: ahar,
+ lunarMonthName: lunarMonthInfo.monthName,
+ isAdhika: lunarMonthInfo.isAdhika,
+ paksha: paksha,
+ tithiName: tithiName,
+ tithiDay: tithiDay
+ };
+}
+
+function getEventsForDate(date, bsYear, bsMonthIndex, bsDay) {
+ var events = [];
+ var gregorianYear = date.getUTCFullYear();
+ var gregorianMonth = date.getUTCMonth() + 1;
+ var gregorianDay = date.getUTCDate();
+ var formattedGregorianDate = formatMonthDay(gregorianMonth, gregorianDay);
+ var formattedBikramRecurringDate = formatMonthDay(bsMonthIndex + 1, bsDay);
+
+ // Handle Gregorian events
+ if (EventsData.gregorianEvents) {
+ for (var i = 0; i < EventsData.gregorianEvents.length; i++) {
+ var event = EventsData.gregorianEvents[i];
+ // Check year constraints using Gregorian year
+ if (event.startYear && gregorianYear < event.startYear) continue;
+ if (event.endYear && gregorianYear > event.endYear) continue;
+
+ if (event.date === formattedGregorianDate) {
+ events.push({ name: event.event, detail: event.detail, holiday: event.holiday });
+ }
+ }
+ }
+
+ // Handle Bikram recurring events
+ if (EventsData.bikramRecurringEvents) {
+ for (var j = 0; j < EventsData.bikramRecurringEvents.length; j++) {
+ let event = EventsData.bikramRecurringEvents[j];
+ // Check year constraints using BS year
+ if (event.startYear && bsYear < event.startYear) continue;
+ if (event.endYear && bsYear > event.endYear) continue;
+
+ if (event.date === formattedBikramRecurringDate) {
+ events.push({ name: event.event, detail: event.detail, holiday: event.holiday });
+ }
+ }
+ }
+
+ // Handle Bikram fixed events
+ if (EventsData.bikramFixedEvents) {
+ var formattedFixedDate = bsYear + "/" + formatMonthDay(bsMonthIndex + 1, bsDay);
+ for(let i = 0; i < EventsData.bikramFixedEvents.length; i++) {
+ let event = EventsData.bikramFixedEvents[i];
+ if (event.date === formattedFixedDate) {
+ events.push({ name: event.event, detail: event.detail, holiday: event.holiday });
+ }
+ }
+ }
+
+ // Handle Lunar events
+ if (EventsData.lunarEvents) {
+ var todayInfo = _getPanchangaBasics(date);
+ if (todayInfo.isAdhika) {
+ return events; // Assuming no lunar events in Adhika masa
+ }
+
+ var yesterday = new Date(date.getTime() - 86400000);
+ var yesterdayInfo = _getPanchangaBasics(yesterday);
+
+ // Check if the *previous* lunar month was a kshaya month. If so, its events are observed in the current month.
+ var prevLunarMonthAhar = todayInfo.ahar - 29.53;
+ var prevMonthStatus = calculateAdhikaMasa(prevLunarMonthAhar);
+ var kshayaMonthName = null;
+ if (prevMonthStatus.startsWith("क्षय")) {
+ kshayaMonthName = prevMonthStatus.split(" ")[1];
+ }
+
+ for (var k = 0; k < EventsData.lunarEvents.length; k++) {
+ var lunarEvent = EventsData.lunarEvents[k];
+
+ // Check year constraints using BS year
+ if (lunarEvent.startYear && bsYear < lunarEvent.startYear) continue;
+ if (lunarEvent.endYear && bsYear > lunarEvent.endYear) continue;
+
+ var isEventForToday = (lunarEvent.lunarMonth === todayInfo.lunarMonthName &&
+ lunarEvent.paksha === todayInfo.paksha &&
+ lunarEvent.tithi === todayInfo.tithiName);
+
+ var isEventFromKshayaMonth = (kshayaMonthName !== null &&
+ lunarEvent.lunarMonth === kshayaMonthName &&
+ lunarEvent.paksha === todayInfo.paksha &&
+ lunarEvent.tithi === todayInfo.tithiName);
+
+ if (isEventForToday || isEventFromKshayaMonth) {
+ var isFirstDayOfTithi = !(yesterdayInfo.lunarMonthName === todayInfo.lunarMonthName &&
+ yesterdayInfo.paksha === todayInfo.paksha &&
+ yesterdayInfo.tithiName === todayInfo.tithiName);
+
+ if (isFirstDayOfTithi) {
+ events.push({ name: lunarEvent.event, detail: lunarEvent.detail, holiday: lunarEvent.holiday });
+ }
+ }
+ }
+ }
+ return events;
+}
+
+
+// Main calculations
+function calculate(date, lat, lon, tz) {
var cacheKey = "panchanga_" + date.getTime();
if (calculationCache[cacheKey]) return calculationCache[cacheKey];
-
var jd = toJulianDay(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
- var ahar = jd - KaliEpoch + 0.25 + ((lon / 15 - tz) / 24);
-
+ var ahar = jd - KaliEpoch + 0.25 + (((lon || 85.3240) / 15 - (tz || 5.75)) / 24);
var sunLong = trueLongitudeSun(ahar);
var moonLong = trueLongitudeMoon(ahar);
var tithiVal = getTithi(sunLong, moonLong);
@@ -350,51 +450,188 @@ function calculate(date, lat = 27.7172, lon = 85.3240, tz = 5.75) {
var paksha = tithiNum <= 15 ? "शुक्ल पक्ष" : "कृष्ण पक्ष";
var tithiDay = tithiNum > 15 ? tithiNum - 15 : tithiNum;
var tithiName = resolveTithiName(tithiDay, paksha);
-
var karanaIdx = Math.floor(2 * tithiVal);
- var karanaName = karanaIdx > 0
- ? (karanaIdx < 57 ? karanas[karanaIdx % 7 || 7] : karanas[karanaIdx - 57 + 8])
- : karanas[0];
-
- var bsInfo = getBikramSambatInfo(ahar, sunLong);
+ var karanaName = karanaIdx > 0 ? (karanaIdx < 57 ? karanas[(karanaIdx-1) % 7 + 1] : karanas[karanaIdx - 57 + 8]) : karanas[0];
+ var bsInfo = toBikramSambat(date);
+ if (!bsInfo) return { error: "Date out of range" };
+ var lunarMonthInfo = getLunarMonthNameWithAdhik(ahar);
+ var events = getEventsForDate(date, bsInfo.year, bsInfo.monthIndex, bsInfo.day);
+ var lunarMonthDisplayName = lunarMonthInfo.isAdhika ? "अधिक " + lunarMonthInfo.monthName : lunarMonthInfo.monthName;
var sunriseSunset = getSunriseSunset(date, lat, lon, tz);
-
- // Determine if the calculation is a fallback
- var isComputed = (bsInfo.year < Bsdata.BS_START_YEAR || bsInfo.year > Bsdata.BS_END_YEAR);
+ var isComputed = bsInfo.isComputed;
+ var adhikaMasa = calculateAdhikaMasa(ahar);
var result = {
gregorianDate: Qt.formatDateTime(date, "dddd, MMMM d, yyyy"),
- bikramSambat: `${bsInfo.year} ${bsInfo.monthName} ${bsInfo.day}`,
+ bikramSambat: toDevanagari(bsInfo.year) + " " + bsInfo.monthName + " " + toDevanagari(bsInfo.day),
bsYear: bsInfo.year,
bsMonthIndex: bsInfo.monthIndex,
bsDay: bsInfo.day,
- monthName: bsInfo.monthName,
- weekday: weekdays[date.getDay()],
+ weekday: weekdays[date.getUTCDay()],
sunrise: sunriseSunset.sunrise,
sunset: sunriseSunset.sunset,
tithi: tithiName,
paksha: paksha,
- nakshatra: nakshatras[Math.floor(moonLong * 27 / 360) % 27],
- yoga: yogas[Math.floor(zero360(sunLong + moonLong) * 27 / 360) % 27],
+ lunarMonth: lunarMonthDisplayName,
+ nakshatra: nakshatras[Math.floor(moonLong / (360 / 27))],
+ yoga: yogas[Math.floor(zero360(sunLong + moonLong) / (360 / 27))],
karana: karanaName,
- sunRashi: rashis[Math.floor(sunLong / 30) % 12],
- moonRashi: rashis[Math.floor(moonLong / 30) % 12],
- adhikaMasa: calculateAdhikaMasa(ahar),
- isComputed: isComputed
+ sunRashi: rashis[Math.floor(sunLong / 30)],
+ moonRashi: rashis[Math.floor(moonLong / 30)],
+ events: events,
+ isComputed: isComputed,
+ adhikaMasa: adhikaMasa
};
calculationCache[cacheKey] = result;
return result;
}
+function getTslong(ahar) {
+ var t1 = (YugaRotation.sun * ahar / YugaCivilDays);
+ t1 -= Math.floor(t1);
+ var mslong = 360 * t1;
+ var x1 = mslong - PlanetApogee.sun;
+ var y1 = PlanetCircumm.sun / 360;
+ var y2 = sinDeg(x1);
+ var y3 = y1 * y2;
+ var x2 = arcsinDeg(y3);
+ var x3 = mslong - x2;
+ return x3;
+}
+
+function todaySauraMasaFirstP(ahar) {
+ var tslong_today = getTslong(ahar);
+ var tslong_tomorrow = getTslong(ahar + 1);
+ tslong_today -= Math.floor(tslong_today / 30) * 30;
+ tslong_tomorrow -= Math.floor(tslong_tomorrow / 30) * 30;
+ return (25 < tslong_today && tslong_tomorrow < 5);
+}
+
+function getSauraMasaDay(ahar) {
+ try {
+ if (todaySauraMasaFirstP(ahar)) {
+ var day = 1;
+ var tslong_tomorrow = getTslong(ahar + 1);
+ var month = Math.floor(tslong_tomorrow / 30) % 12;
+ month = (month + 12) % 12;
+ return { m: month, d: day };
+ } else {
+ var yesterday = getSauraMasaDay(ahar - 1);
+ return { m: yesterday.m, d: yesterday.d + 1 };
+ }
+ } catch (e) {
+ return { m: 0, d: 1 };
+ }
+}
+
+function fromGregorianAstronomical(gYear, gMonth, gDay) {
+ var julian = toJulianDay(gYear, gMonth - 1, gDay);
+ var ahar = julian - KaliEpoch;
+ var sauraMasaResult = getSauraMasaDay(ahar);
+ var saura_masa_num = sauraMasaResult.m;
+ var saura_masa_day = sauraMasaResult.d;
+ var YearKali = Math.floor(ahar * YugaRotation.sun / YugaCivilDays);
+ var YearSaka = YearKali - 3179;
+ var nepalimonth = saura_masa_num % 12;
+ var year = YearSaka + 135 + Math.floor((saura_masa_num - nepalimonth) / 12);
+ var month = (saura_masa_num + 12) % 12 + 1;
+ return {
+ year: year,
+ monthIndex: month - 1,
+ day: saura_masa_day,
+ monthName: solarMonths[month - 1]
+ };
+}
+
+function calculateAdhikaMasa(ahar) {
+ var lunarMonthStart = findNewMoon(ahar);
+ if (lunarMonthStart > ahar) {
+ lunarMonthStart = findNewMoon(lunarMonthStart - 29.530588853);
+ }
+ var lunarMonthEnd = findNewMoon(lunarMonthStart + 29.530588853);
+ var sunLongStart = trueLongitudeSun(lunarMonthStart);
+ var sunLongEnd = trueLongitudeSun(lunarMonthEnd);
+ var startSign = Math.floor(sunLongStart / 30);
+ var endSign = Math.floor(sunLongEnd / 30);
+ var signCrossings = 0;
+ var currentSign = startSign;
+ for (var i = 1; i <= 29; i++) {
+ var checkAhar = lunarMonthStart + i;
+ var checkSunLong = trueLongitudeSun(checkAhar);
+ var checkSign = Math.floor(checkSunLong / 30);
+ if (checkSign < currentSign) {
+ checkSign += 12;
+ }
+ if (checkSign > currentSign) {
+ signCrossings += (checkSign - currentSign);
+ currentSign = checkSign % 12;
+ }
+ }
+ if (endSign < currentSign) {
+ endSign += 12;
+ }
+ if (endSign > currentSign) {
+ signCrossings += (endSign - currentSign);
+ }
+ if (signCrossings === 0) {
+ return "अधिक " + solarMonths[startSign];
+ }
+ if (signCrossings >= 2) {
+ var skippedSign = (startSign + 1) % 12;
+ return "क्षय " + solarMonths[skippedSign];
+ }
+ return "छैन";
+}
+
+function getLunarMonthNameWithAdhik(ahar) {
+ var lunarMonthStart = findNewMoon(ahar);
+ if (lunarMonthStart > ahar) {
+ lunarMonthStart = findNewMoon(lunarMonthStart - 29.53);
+ }
+ var purnima = findPurnima(lunarMonthStart + 14.77);
+ var sunLongPurnima = trueLongitudeSun(purnima);
+ var purnimaSign = Math.floor(sunLongPurnima / 30);
+ var signCrossings = 0;
+ var currentSign = Math.floor(trueLongitudeSun(lunarMonthStart) / 30);
+ for (var i = 1; i <= 29; i++) {
+ var checkAhar = lunarMonthStart + i;
+ var checkSunLong = trueLongitudeSun(checkAhar);
+ var checkSign = Math.floor(checkSunLong / 30);
+ if (checkSign < currentSign) {
+ checkSign += 12;
+ }
+ if (checkSign > currentSign) {
+ signCrossings += (checkSign - currentSign);
+ currentSign = checkSign % 12;
+ }
+ }
+ var isAdhika = (signCrossings === 0);
+ var result = {
+ monthName: solarMonths[purnimaSign],
+ isAdhika: isAdhika
+ };
+ return result;
+}
-// --- Debug calculations. ---
-function generateDebugInfo(date, lat = 27.7172, lon = 85.3240, tz = 5.75) {
+// Debug calculations
+function generateDebugInfo(date, lat, lon, tz) {
+ lat = lat || 27.7172;
+ lon = lon || 85.3240;
+ tz = tz || 5.75;
var cacheKey = "debug_" + date.getTime();
- if (calculationCache[cacheKey]) return calculationCache[cacheKey];
+ if (calculationCache[cacheKey]) return calculationCache[cacheKey];
+ var bsInfoData = toBikramSambat(date, lon, tz);
- var jd = toJulianDay(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
- var ahar = jd - KaliEpoch + 0.25 + ((lon / 15 - tz) / 24);
+ if (!bsInfoData) {
+ return { debug: "Date out of pre-calculated range." };
+ }
+ var jd = toJulianDay(
+ date.getUTCFullYear(),
+ date.getUTCMonth(),
+ date.getUTCDate()
+ );
+ var ahar = jd - KaliEpoch + 0.25 + ((lon / 15 - tz) / 24);
var sunLong = trueLongitudeSun(ahar);
var moonLong = trueLongitudeMoon(ahar);
var tithiVal = getTithi(sunLong, moonLong);
@@ -404,35 +641,111 @@ function generateDebugInfo(date, lat = 27.7172, lon = 85.3240, tz = 5.75) {
var tithiName = resolveTithiName(tithiDay, paksha);
var karanaIdx = Math.floor(2 * tithiVal);
- var karanaName = karanaIdx > 0
- ? (karanaIdx < 57 ? karanas[karanaIdx % 7 || 7] : karanas[karanaIdx - 57 + 8])
- : karanas[0];
+ var karanaName = karanaIdx > 0 ? (karanaIdx < 57 ? karanas[(karanaIdx - 1) % 7 + 1] : karanas[karanaIdx - 57 + 8]) : karanas[0];
+
+ var bsInfoCalc = fromGregorianAstronomical(
+ date.getUTCFullYear(),
+ date.getUTCMonth() + 1,
+ date.getUTCDate()
+ );
+ var dayDifference = 0;
+ var gregFromAstronomical_Today = fromBikramSambat(
+ bsInfoCalc.year,
+ bsInfoCalc.monthIndex,
+ bsInfoCalc.day
+ );
+
+ if (gregFromAstronomical_Today) {
+ var timeDiff = date.getTime() - gregFromAstronomical_Today.getTime();
+ dayDifference = Math.round(timeDiff / (1000 * 60 * 60 * 24));
+ }
+
+ if (dayDifference === 0 && !bsInfoData.isComputed) {
+ var isMismatch = (bsInfoData.day !== bsInfoCalc.day || bsInfoData.monthIndex !== bsInfoCalc.monthIndex);
+ if (isMismatch) {
+ var tomorrow = new Date(date.getTime());
+ tomorrow.setUTCDate(date.getUTCDate() + 1);
+ var bsInfoCalc_Tomorrow = fromGregorianAstronomical(
+ tomorrow.getUTCFullYear(),
+ tomorrow.getUTCMonth() + 1,
+ tomorrow.getUTCDate()
+ );
+
+ var gregFromAstronomical_Tomorrow = fromBikramSambat(
+ bsInfoCalc_Tomorrow.year,
+ bsInfoCalc_Tomorrow.monthIndex,
+ bsInfoCalc_Tomorrow.day
+ );
+
+ if (gregFromAstronomical_Tomorrow) {
+ var timeDiff_Tomorrow = tomorrow.getTime() - gregFromAstronomical_Tomorrow.getTime();
+ var dayDifference_Tomorrow = Math.round(timeDiff_Tomorrow / (1000 * 60 * 60 * 24));
+
+ if (dayDifference_Tomorrow !== 0) {
+ dayDifference = dayDifference_Tomorrow;
+ }
+ }
+ }
+ }
+
+ var dayDifferenceDisplay = dayDifference;
+ if (Math.abs(dayDifference) > 2) {
+ dayDifferenceDisplay = '' + dayDifference + '';
+ } else if (Math.abs(dayDifference) > 0) {
+ dayDifferenceDisplay = '' + dayDifference + '';
+ }
- var bsInfo = getBikramSambatInfo(ahar, sunLong);
var sunriseSunset = getSunriseSunset(date, lat, lon, tz);
- var isComputed = (bsInfo.year < Bsdata.BS_START_YEAR || bsInfo.year > Bsdata.BS_END_YEAR);
-
- var debugOutput = `
- Debug Information (Surya Siddhanta):
- gregorianDate: ${Qt.formatDateTime(date, "dddd, MMMM d, yyyy")}
- bikramSambat: ${toDevanagari(bsInfo.year)} ${bsInfo.monthName} ${toDevanagari(bsInfo.day)}
- bsMonthIndex: ${bsInfo.monthIndex}
- weekday(local): ${weekdays[date.getDay()]}
- sunrise: ${sunriseSunset.sunrise}
- sunset: ${sunriseSunset.sunset}
- tithi: ${tithiName} | index: ${tithiDay}
- tithiAngle: ${(tithiVal * 12).toFixed(4)}°
- paksha: ${paksha}
- nakshatra: ${nakshatras[Math.floor(moonLong * 27 / 360) % 27]} | index: ${Math.floor(moonLong * 27 / 360) % 27 + 1}
- yoga: ${yogas[Math.floor(zero360(sunLong + moonLong) * 27 / 360) % 27]} | index: ${Math.floor(zero360(sunLong + moonLong) * 27 / 360) % 27 + 1}
- yogaAngle: ${zero360(sunLong + moonLong).toFixed(4)}°
- karana: ${karanaName} | index: ${karanaIdx}
- karanaAngle: ${(2 * tithiVal).toFixed(4)}
- sunRashi: ${rashis[Math.floor(sunLong / 30) % 12]} | index: ${Math.floor(sunLong / 30) % 12 + 1}
- moonRashi: ${rashis[Math.floor(moonLong / 30) % 12]} | index: ${Math.floor(moonLong / 30) % 12 + 1}
- adhikaMasa: ${calculateAdhikaMasa(ahar)}
- isComputed: ${isComputed}
- `.trim();
-
- return { debug: debugOutput };
+ var isComputed = bsInfoData.isComputed;
+ var computedbsdate = toDevanagari(bsInfoCalc.year) + " " + bsInfoCalc.monthName + " " + toDevanagari(bsInfoCalc.day);
+ var dataDrivenBsDateString = "N/A";
+ var acceptedBsDate = computedbsdate;
+ var dataDrivenInfo = "";
+
+ if (bsInfoData && !isComputed) {
+ dataDrivenBsDateString = toDevanagari(bsInfoData.year) + " " + bsInfoData.monthName + " " + toDevanagari(bsInfoData.day);
+ acceptedBsDate = dataDrivenBsDateString;
+ dataDrivenInfo = `Debug Information\n(Data-Driven for date conversion):\n` + "Gregorian Date: " + date + "\n" + "accepted BS Date: " + acceptedBsDate + "\n" + "(panchanga is based on data-driven conversion)\n";
+ } else {
+ dataDrivenInfo = `Debug Information\n(Astronomical Calculation - outside data range):\n` + "Gregorian Date: " + date + "\n" + "accepted BS Date: " + acceptedBsDate + "\n" + "(panchanga is based on astronomical computation)\n";
+ }
+
+ var lunarMonthInfo = getLunarMonthNameWithAdhik(ahar);
+ var lunarMonthDisplay = lunarMonthInfo.isAdhika ? "अधिक " + lunarMonthInfo.monthName + " " + paksha : lunarMonthInfo.monthName + " " + paksha;
+
+ var debugOutput =
+ '' +
+ dataDrivenInfo +
+ `Consistency Check:\n` +
+ "Data-Driven BS Date: " + dataDrivenBsDateString + "\n" +
+ "Astronomical BS Date (Computed): " + computedbsdate + "\n" +
+ "Day Difference: " + dayDifferenceDisplay + " " + (Math.abs(dayDifference) === 1 ? 'day' : 'days') + "\n" +
+ `Note: Positive = Astronomical date is behind;\n` +
+ `Negative = Astronomical date is ahead\n` +
+ " " + `Lunar Month (Purnimanta): ` + lunarMonthDisplay + "\n" +
+ `--- Solar Outputs ---\n` +
+ "gregorianDate: " + Qt.formatDateTime(date, "dddd, MMMM d, yyyy") + "\n" +
+ "sunrise: " + sunriseSunset.sunrise + "\n" + "sunset: " + sunriseSunset.sunset + "\n" +
+ "sunRashi: " + rashis[Math.floor(sunLong / 30) % 12] + " | index: " + (Math.floor(sunLong / 30) % 12 + 1) + "\n" +
+ " " + `--- Lunar Outputs ---\n` +
+ "tithi: " + tithiName + " | index: " + tithiDay + "\n" +
+ "tithiAngle: " + (tithiVal * 12).toFixed(4) + "°\n" + "paksha: " + paksha + "\n" +
+ "nakshatra: " + nakshatras[Math.floor(moonLong / (360 / 27))] + " | index: " + (Math.floor(moonLong / (360 / 27)) + 1) + "\n" +
+ "yoga: " + yogas[Math.floor(zero360(sunLong + moonLong) / (360 / 27))] + " | index: " + (Math.floor(zero360(sunLong + moonLong) / (360 / 27)) + 1) + "\n" +
+ "yogaAngle: " + zero360(sunLong + moonLong).toFixed(4) + "°\n" +
+ "karana: " + karanaName + " | index: " + karanaIdx + "\n" +
+ "karanaAngle: " + (2 * tithiVal).toFixed(4) + "\n" +
+ "moonRashi: " + rashis[Math.floor(moonLong / 30) % 12] + " | index: " + (Math.floor(moonLong / 30) % 12 + 1) + "\n" +
+ "adhikaMasa: " + calculateAdhikaMasa(ahar) + " (computed)\n" +
+
+ " " + `--- Metadata ---\n` +
+ "Julian Day: " + jd.toFixed(4) + "\n" +
+ "Ahar: " + ahar.toFixed(4) + "\n" +
+ "weekday (UTC): " + weekdays[date.getUTCDay()] + "\n" +
+ "isComputed: " + isComputed +
+ '
';
+
+ var result = { debug: debugOutput.trim().replace(/^\s*[\r\n]/gm, "") };
+ calculationCache[cacheKey] = result;
+ return result;
}