diff --git a/qml/CalendarGrid.qml b/qml/CalendarGrid.qml index 6d9cbc1..897c6bc 100644 --- a/qml/CalendarGrid.qml +++ b/qml/CalendarGrid.qml @@ -4,64 +4,127 @@ import QtQuick.Controls 2.15 import "qrc:/qml/" // CalendarGrid.qml -GridLayout { - id: calendarGrid - columns: 7 - rowSpacing: 0 - columnSpacing: 0 +ColumnLayout { + id: calendarGridRoot Layout.fillWidth: true - Layout.fillHeight: true - Layout.margins: 20 - Layout.topMargin: 10 + // Properties property alias calendarModel: repeater.model + property alias eventListModel: eventLabel.eventModel property var theme + // Signals signal dayClicked(var panchanga) - Repeater { - id: repeater - delegate: Item { - Layout.fillWidth: true - Layout.fillHeight: modelData.type === "day" || modelData.type === "empty" - Layout.preferredHeight: modelData.type === "header" ? 35 : -1 + // Components - Loader { - id: delegateLoader - anchors.fill: parent + // Calendar grid layout + GridLayout { + id: calendarGrid + columns: 7 + rowSpacing: 0 + columnSpacing: 0 + Layout.fillWidth: true + Layout.preferredHeight: parent.width + Layout.margins: 10 + Layout.topMargin: 10 + + Repeater { + id: repeater + delegate: Item { + Layout.fillWidth: true + Layout.fillHeight: modelData.type === "day" || modelData.type === "empty" + Layout.preferredHeight: modelData.type === "header" ? 35 : -1 + + Loader { + id: delegateLoader + anchors.fill: parent + + sourceComponent: { + if (modelData.type === "header") { + return headerComponent; + } else if (modelData.type === "day") { + return dayComponent; + } else { + return emptyComponent; + } + } + + onLoaded: { + if (modelData.type === "day") { + item.bsDay = modelData.bsDay; + item.adDay = modelData.adDay; + item.tithi = modelData.tithi; + item.isToday = modelData.isToday; + item.isSaturday = modelData.isSaturday; + item.hasEvent = modelData.hasEvent; + item.theme = calendarGridRoot.theme; + + var isHoliday = false; + if (modelData.panchanga && modelData.panchanga.events) { + for (var i = 0; i < modelData.panchanga.events.length; i++) { + if (modelData.panchanga.events[i].holiday) { + isHoliday = true; + break; + } + } + } + item.isHoliday = isHoliday; - sourceComponent: { - if (modelData.type === "header") { - return headerComponent; - } else if (modelData.type === "day") { - return dayComponent; - } else { // for "empty" type - return emptyComponent; + item.clicked.connect(function() { + calendarGridRoot.dayClicked(modelData.panchanga) + }); + } else if (modelData.type === "header") { + item.text = modelData.text; + item.theme = calendarGridRoot.theme; + item.cellIndex = index; + } } } + } + } + } + + // Event List Footer + Rectangle { + id: eventFooter + Layout.fillWidth: true + implicitHeight: eventLabel.paintedHeight + 20 + color: theme.secondaryBg + radius: 6 + visible: eventLabel.eventModel && eventLabel.eventModel.length > 0 + + Label { + id: eventLabel + anchors.fill: parent + anchors.margins: 10 + + property var eventModel: [] // Model reference - onLoaded: { - if (modelData.type === "day") { - item.bsDay = modelData.bsDay; - item.adDay = modelData.adDay; - item.tithi = modelData.tithi; - item.isToday = modelData.isToday; - item.isSaturday = modelData.isSaturday; - item.theme = calendarGrid.theme; - item.clicked.connect(function() { - calendarGrid.dayClicked(modelData.panchanga) - }); - } else if (modelData.type === "header") { - item.text = modelData.text; - item.theme = calendarGrid.theme; - item.cellIndex = index; + text: { + var str = ""; + if (eventModel) { + for (var i = 0; i < eventModel.length; i++) { + var item = eventModel[i]; + str += item.bsDay + " :\u00A0" + item.eventName; + if (i < eventModel.length - 1) { + str += "  •  "; + } } } + return str; } + + font.pixelSize: 12 + color: theme ? theme.secondaryText : "black" + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter } } + // Component Definitions Component { id: headerComponent Item { @@ -78,56 +141,15 @@ GridLayout { border.color: theme.borderColor } - Rectangle { - visible: cellIndex === 0 - width: headerBackground.radius - height: headerBackground.radius - color: headerBackground.color - anchors.top: parent.top - anchors.right: parent.right - } - Rectangle { - visible: cellIndex === 0 - width: headerBackground.radius - height: headerBackground.radius - color: headerBackground.color - anchors.bottom: parent.bottom - anchors.left: parent.left - } - Rectangle { - visible: cellIndex === 6 - width: headerBackground.radius - height: headerBackground.radius - color: headerBackground.color - anchors.top: parent.top - anchors.left: parent.left - } - Rectangle { - visible: cellIndex === 6 - width: headerBackground.radius - height: headerBackground.radius - color: headerBackground.color - anchors.bottom: parent.bottom - anchors.left: parent.left - } - Rectangle { - visible: cellIndex === 0 || cellIndex === 6 - width: headerBackground.radius - height: headerBackground.radius - color: headerBackground.color - anchors.bottom: parent.bottom - anchors.right: parent.right - } + Rectangle { visible: cellIndex === 0; width: headerBackground.radius; height: headerBackground.radius; color: headerBackground.color; anchors.top: parent.top; anchors.right: parent.right } + Rectangle { visible: cellIndex === 0; width: headerBackground.radius; height: headerBackground.radius; color: headerBackground.color; anchors.bottom: parent.bottom; anchors.left: parent.left } + Rectangle { visible: cellIndex === 6; width: headerBackground.radius; height: headerBackground.radius; color: headerBackground.color; anchors.top: parent.top; anchors.left: parent.left } + Rectangle { visible: cellIndex === 6; width: headerBackground.radius; height: headerBackground.radius; color: headerBackground.color; anchors.bottom: parent.bottom; anchors.left: parent.left } + Rectangle { visible: cellIndex === 0 || cellIndex === 6; width: headerBackground.radius; height: headerBackground.radius; color: headerBackground.color; anchors.bottom: parent.bottom; anchors.right: parent.right } Label { text: headerItemContainer.text - color: { - if (cellIndex === 6) { - return "#E4080A" - } else { - return theme.accentText; - } - } + color: (cellIndex === 6) ? "#E4080A" : theme.accentText font.bold: true font.pixelSize: 14 anchors.centerIn: parent diff --git a/qml/CalendarLogic.qml b/qml/CalendarLogic.qml index f2dec23..1fe9ed5 100644 --- a/qml/CalendarLogic.qml +++ b/qml/CalendarLogic.qml @@ -14,6 +14,7 @@ QtObject { property int currentBsYear: 2081 property int currentBsMonthIndex: 1 property var calendarModel: [] + property var currentMonthEvents: [] // Holds events for the current month property string currentBsLabelStr: "" property string currentAdLabelStr: "" property string prevMonthName: "" @@ -59,7 +60,9 @@ QtObject { } function renderCalendarByBs(year, monthIndex, preserveAdState = false) { - calendarModel = []; + var newCalendarModel = []; + var eventsByDay = {}; // Use an object to group events by day + currentBsYear = year; currentBsMonthIndex = monthIndex; var info = Panchanga.getBikramMonthInfo(year, monthIndex); @@ -69,23 +72,18 @@ QtObject { } if (!preserveAdState) { - // LOGIC to determine the representative AD month var bsMonthStartDate = Panchanga.fromBikramSambat(year, monthIndex, 1); var startAdYear = bsMonthStartDate.getUTCFullYear(); var startAdMonth = bsMonthStartDate.getUTCMonth(); - var nextAdMonth = (startAdMonth + 1) % 12; var nextAdYear = (startAdMonth === 11) ? startAdYear + 1 : startAdYear; var firstOfNextAdMonth = new Date(Date.UTC(nextAdYear, nextAdMonth, 1)); - var bsMonthEndDate = Panchanga.fromBikramSambat(year, monthIndex, info.totalDays); if (firstOfNextAdMonth <= bsMonthEndDate) { - // Prefer the later AD month if its first day is in the BS month. currentAdYear = nextAdYear; currentAdMonth = nextAdMonth; } else { - // Otherwise, use the starting AD month. currentAdYear = startAdYear; currentAdMonth = startAdMonth; } @@ -98,13 +96,12 @@ QtObject { var daysInMonth = info.totalDays; var startDay = info.startDayOfWeek; - var model = []; var weekdaysNe = ["आइतबार", "सोमबार", "मङ्गलबार", "बुधबार", "बिहीबार", "शुक्रबार", "शनिबार"]; for (var i = 0; i < 7; ++i) { - model.push({ type: "header", text: weekdaysNe[i] }); + newCalendarModel.push({ type: "header", text: weekdaysNe[i] }); } for (i = 0; i < startDay; ++i) { - model.push({ type: "empty" }); + newCalendarModel.push({ type: "empty" }); } for (var day = 1; day <= daysInMonth; ++day) { var adDate = Panchanga.fromBikramSambat(year, monthIndex, day); @@ -114,14 +111,45 @@ QtObject { } var isToday = adDate.toDateString() === new Date().toDateString(); var isSaturday = (startDay + day - 1) % 7 === 6; + var hasEventForDay = result.events && result.events.length > 0; + + if (hasEventForDay) { + var devanagariDay = toDevanagari(day); + if (!eventsByDay[devanagariDay]) { + eventsByDay[devanagariDay] = []; // Initialize array if it doesn't exist + } + for (var j = 0; j < result.events.length; j++) { + eventsByDay[devanagariDay].push(result.events[j].name); + } + } + result.monthName = info.monthName; - model.push({ + newCalendarModel.push({ type: "day", bsDay: day, adDay: adDate.getDate(), tithi: result.tithi, isToday: isToday, isSaturday: isSaturday, + hasEvent: hasEventForDay, gregorianDate: result.gregorianDate, panchanga: result }); } - calendarModel = model; + + // Process the grouped events + var newMonthEvents = []; + // Sort the days to ensure events are listed in chronological order + var sortedDays = Object.keys(eventsByDay).sort(function(a, b) { + return parseInt(fromDevanagari(a)) - parseInt(fromDevanagari(b)); + }); + + for (i = 0; i < sortedDays.length; i++) { + var bsDay = sortedDays[i]; + newMonthEvents.push({ + bsDay: bsDay, + eventName: eventsByDay[bsDay].join(", ") + }); + } + + calendarModel = newCalendarModel; + currentMonthEvents = newMonthEvents; + currentBsLabelStr = toDevanagari(year) + " " + info.monthName; currentAdLabelStr = getGregorianRange(year, monthIndex); var prevMonthIndex = monthIndex - 1; @@ -143,10 +171,8 @@ QtObject { function renderCalendarByAd(year, monthIndex) { currentAdYear = year; currentAdMonth = monthIndex; - var date = new Date(Date.UTC(year, monthIndex, 1)); var bsInfo = Panchanga.calculate(date); - renderCalendarByBs(bsInfo.bsYear, bsInfo.bsMonthIndex, true); } diff --git a/qml/DayCell.qml b/qml/DayCell.qml index af2e323..4ffa207 100644 --- a/qml/DayCell.qml +++ b/qml/DayCell.qml @@ -12,13 +12,18 @@ Rectangle { property string tithi: "" property bool isToday: false property bool isSaturday: false + property bool hasEvent: false + property bool isHoliday: false property var theme + property var cellDate signal clicked() color: { if (!theme) return "white"; if (cellMouseArea.containsMouse) return theme.tertiaryBg; + //holiday background disabled now + //if (isHoliday) return theme.holidayBg; if (isSaturday) return theme.saturdayBg; if (isToday) return theme.todayBg; return theme.secondaryBg; @@ -28,10 +33,40 @@ Rectangle { if (!theme) return "grey"; if (isToday) return theme.todayBorder; if (isSaturday) return theme.saturdayBorder; + if (isHoliday) return theme.holidayBorder; return theme.borderColor; } border.width: isToday ? 2 : 1 + Component.onCompleted: { + if (cellDate) { + var dayInfo = Panchanga.calculate(cellDate); + if (dayInfo.events && dayInfo.events.length > 0) { + for (var i = 0; i < dayInfo.events.length; i++) { + if (dayInfo.events[i].holiday) { + isHoliday = true; + break; + } + } + } + } + } + + + // Event Indicator + Rectangle { + id: eventIndicator + width: 6 + height: 6 + radius: 4 + color: "orange" + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: 5 + visible: hasEvent + z: 1 + } + Label { text: Panchanga.toDevanagari(bsDay || 0) font.bold: true diff --git a/qml/EventDisplay.qml b/qml/EventDisplay.qml new file mode 100644 index 0000000..2ede6fa --- /dev/null +++ b/qml/EventDisplay.qml @@ -0,0 +1,36 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +// EventDisplay.qml (used in PanchangaDetailDialog +ColumnLayout { + id: eventRoot + Layout.fillWidth: true + spacing: 8 + + // Properties + property var theme + property var events: [] + + // Components + + + + //Label to display all events, joined by commas. + Label { + text: { + if (events.length > 0) { + return events.map(function(event) { + return event.name + (event.detail ? ": " + event.detail : ""); + }).join(", "); + } + return ""; + } + font.pixelSize: 12 + color: theme ? theme.primaryText : "black" + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + Layout.fillWidth: true + visible: events.length > 0 + } +} diff --git a/qml/PanchangaDetailDialog.qml b/qml/PanchangaDetailDialog.qml index 9573ea6..1a524a6 100644 --- a/qml/PanchangaDetailDialog.qml +++ b/qml/PanchangaDetailDialog.qml @@ -6,109 +6,74 @@ import "qrc:/qml/" // PanchangaDetailDialog.qml Dialog { - id: panchangaDetailDialogRoot // Changed id to avoid name collision + id: panchangaDetailDialogRoot width: Math.min(parent.width * 0.9, 600) - height: Math.min(parent.height * 0.8, 550) + height: Math.min(contentItem.implicitHeight, parent.height) modal: true closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside x: (parent.width - width) / 2 y: (parent.height - height) / 2 padding: 0 + background: null + // Properties property var theme property var panchangaData: null property bool debugVisible: false property string currentDebugInfo: "" - background: Rectangle { + // Components + contentItem: Rectangle { + id: contentRect + width: panchangaDetailDialogRoot.width + implicitHeight: contentColumn.implicitHeight color: theme ? theme.secondaryBg : "white" radius: 12 border.color: theme ? theme.borderColor : "grey" border.width: 1 clip: true - } - contentItem: Item { - width: panchangaDetailDialogRoot.width - height: panchangaDetailDialogRoot.height - - Rectangle { - id: modalHeader + ColumnLayout { + id: contentColumn width: parent.width - height: 60 - color: theme ? theme.secondaryBg : "white" - - Label { - id: modalTitle - text: "दिनको विवरण" - font.pixelSize: 20 - font.bold: true - color: theme ? theme.modalHeaderText : "black" - anchors.centerIn: parent - z: 1 - } - } - Rectangle { - id: modalFooter - width: parent.width - height: 65 - anchors.bottom: parent.bottom - anchors.bottomMargin: 12 - color: theme ? theme.secondaryBg : "white" - - Button { - text: "बन्द गर्नुहोस्" - anchors.centerIn: parent - onClicked: panchangaDetailDialogRoot.close() - - background: Rectangle { - color: theme ? theme.tertiaryBg : "lightgrey" - border.color: parent.hovered && theme ? theme.accent : "gray" - border.width: parent.hovered ? 2 : 1 - radius: 12 - } + // Header section of the dialog. + Rectangle { + id: modalHeader + Layout.fillWidth: true + height: 60 + color: "transparent" - contentItem: Text { - text: parent.text - font: parent.font - color: theme ? theme.primaryText : "black" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - padding: 5 + Label { + id: modalTitle + text: "दिनको विवरण" + font.pixelSize: 20 + font.bold: true + color: theme ? theme.modalHeaderText : "black" + anchors.centerIn: parent + z: 1 } } - } - - Flickable { - id: flickableArea - anchors { - left: parent.left - right: parent.right - top: modalHeader.bottom - bottom: modalFooter.top - leftMargin: 30 - rightMargin: 20 - } - clip: false - contentHeight: scrollContent.height + // The main content area. ColumnLayout { - id: scrollContent - width: parent.width + id: contentArea + Layout.fillWidth: true spacing: 15 Button { id: showDebugButton - visible: !window.debugVisible + visible: !panchangaDetailDialogRoot.debugVisible text: "डिबग जानकारी देखाउनुहोस्" Layout.fillWidth: true height: 40 - onClicked: debugVisible = true + onClicked: panchangaDetailDialogRoot.debugVisible = true + Layout.leftMargin: 30 + Layout.rightMargin: 20 background: Rectangle { color: theme.tertiaryBg - border.color: parent.hovered ? theme.accent : "gray" + border.color: parent.hovered ? theme.accent : "gray" border.width: parent.hovered ? 2 : 1 radius: 12 } @@ -123,51 +88,64 @@ Dialog { } } + // Layout for the Panchanga details. ColumnLayout { id: panchangaDetails - visible: !debugVisible - spacing: 15 + visible: !panchangaDetailDialogRoot.debugVisible + spacing: 8 Layout.fillWidth: true + Layout.leftMargin: 30 + Layout.rightMargin: 20 } Rectangle { id: debugInfoArea - visible: debugVisible + visible: panchangaDetailDialogRoot.debugVisible z: 99 Layout.fillWidth: true - implicitHeight: debugInfoText.paintedHeight + 10 + implicitHeight: debugInfoText.paintedHeight + 20 color: "black" border.color: theme ? theme.accent : "blue" border.width: 1 radius: 8 + Layout.leftMargin: 30 + Layout.rightMargin: 20 Flickable { anchors.fill: parent - contentWidth: debugInfoText.paintedWidth - contentHeight: debugInfoText.paintedHeight + 10 + contentWidth: Math.min(parent.width * 0.96, 600) + contentHeight: parent.height clip: true - TextEdit { - id: debugInfoText - text: currentDebugInfo - color: "white" - font.family: "monospace" - font.pointSize: 9 - readOnly: true - wrapMode: TextEdit.NoWrap - selectByMouse: true - padding: 10 + ScrollView { + anchors.fill: parent + clip: true + + TextEdit { + id: debugInfoText + text: currentDebugInfo + color: "white" + font.family: "monospace" + font.pointSize: 8 + readOnly: true + wrapMode: TextEdit.NoWrap + textFormat: TextEdit.RichText + selectByMouse: true + padding: 10 + } } } } Button { id: hideDebugButton - visible: debugVisible + visible: panchangaDetailDialogRoot.debugVisible text: "डिबग लुकाउनुहोस्" Layout.fillWidth: true height: 40 - onClicked: debugVisible = false + onClicked: panchangaDetailDialogRoot.debugVisible = false + Layout.leftMargin: 30 + Layout.rightMargin: 20 background: Rectangle { radius: 8 @@ -184,13 +162,56 @@ Dialog { } } } + + // Footer section with the close button. + Rectangle { + id: modalFooter + Layout.fillWidth: true + height: 65 + color: "transparent" + + Button { + text: "बन्द गर्नुहोस्" + anchors.centerIn: parent + onClicked: panchangaDetailDialogRoot.close() + + background: Rectangle { + color: theme ? theme.tertiaryBg : "lightgrey" + border.color: parent.hovered && theme ? theme.accent : "gray" + border.width: parent.hovered ? 2 : 1 + radius: 12 + } + + contentItem: Text { + text: parent.text + font: parent.font + color: theme ? theme.primaryText : "black" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + padding: 5 + } + } + } } } + + // Handlers and Functions onOpened: { if (panchangaData) { clearPanchangaDetails(); modalTitle.text = panchangaData.gregorianDate; + + var eventComponent = Qt.createComponent("EventDisplay.qml"); + if (eventComponent.status === Component.Ready) { + var eventObj = eventComponent.createObject(panchangaDetails, { + "theme": theme, + "events": panchangaData.events + }); + } else { + console.log("Error creating event component:", eventComponent.errorString()); + } + var details = [ createDetailRow("बिक्रम संवत", toDevanagari(panchangaData.bsYear) + " " + panchangaData.monthName + " " + toDevanagari(panchangaData.bsDay)), createDetailRow("वार", panchangaData.weekday), diff --git a/qml/Theme.qml b/qml/Theme.qml index f480933..9444585 100644 --- a/qml/Theme.qml +++ b/qml/Theme.qml @@ -35,10 +35,12 @@ QtObject { readonly property color borderColor: isDark ? "#334155" : "#e2e8f0" readonly property color todayBorder: isDark ? "#0ea5e9" : "#0284c7" readonly property color saturdayBorder: isDark ? "#334155" : "#fda4af" + readonly property color holidayBorder: isDark ? "#C58F69" : "#FF9101" // === Highlighted Days === readonly property color todayBg: isDark ? "#082f49" : "#f0f9ff" readonly property color saturdayBg: isDark ? "#6E3233" : "#FEDDDF" + //readonly property color holidayBg: isDark ? "#6E3233" : "#FEDDDF" readonly property color adDayText: isDark ? "#BBCFFA" : "#6E9BFD" // === Modal / Dialogs === diff --git a/qml/main.qml b/qml/main.qml index 947fd9d..3049f3f 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -322,8 +322,10 @@ ApplicationWindow { panchangaDetailDialog.panchangaData = panchanga panchangaDetailDialog.open() } + eventListModel: calendarLogic.currentMonthEvents } + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 100 diff --git a/resources.qrc b/resources.qrc index 0a549c2..8688035 100644 --- a/resources.qrc +++ b/resources.qrc @@ -15,5 +15,7 @@ qml/NavigationBar.qml qml/PanchangaDetailDialog.qml qml/Theme.qml + resources/EventData.js + qml/EventDisplay.qml diff --git a/resources/EventData.js b/resources/EventData.js new file mode 100644 index 0000000..9887b26 --- /dev/null +++ b/resources/EventData.js @@ -0,0 +1,627 @@ +// EventsData.js +// This file contains event data for the Panchanga Calculator, organized by type. +// All events have a consistent structure including a 'holiday' boolean. + +.pragma library + +// Lunar based events, defined by month, fortnight (paksha), and lunar day (tithi). +var lunarEvents = [ + { + event: "चैते दशैँ", + eventType: "lunar", + lunarMonth: "चैत्र", + paksha: "शुक्ल पक्ष", + tithi: "अष्टमी", + startYear: null, + endYear: null, + detail: "चैत्र महिनामा मनाइने दशैँ, ठूलो दशैँको सानो रूप।", + holiday: true + }, + { + event: "रामनवमी", + eventType: "lunar", + lunarMonth: "चैत्र", + paksha: "शुक्ल पक्ष", + tithi: "नवमी", + startYear: null, + endYear: null, + detail: "भगवान रामको जन्मदिन।", + holiday: true + }, + { + event: "मातातीर्थ औंसी", + eventType: "lunar", + lunarMonth: "चैत्र", + paksha: "कृष्ण पक्ष", + tithi: "अमावस्या", + startYear: null, + endYear: null, + detail: "आमाको मुख हेर्ने दिन。", + holiday: true + }, + { + event: "घोडेजात्रा", + eventType: "lunar", + lunarMonth: "चैत्र", + paksha: "कृष्ण पक्ष", + tithi: "अमावस्या", + startYear: null, + endYear: null, + detail: "काठमाडौँमा मनाइने घोडे जात्रा उत्सव।", + holiday: true + }, + { + event: "अक्षय तृतीया", + eventType: "lunar", + lunarMonth: "वैशाख", + paksha: "शुक्ल पक्ष", + tithi: "तृतीया", + startYear: null, + endYear: null, + detail: "जौ सातु खाने दिन。", + holiday: false + }, + { + event: "बुद्ध जयन्ती / उभौली पर्व", + eventType: "lunar", + lunarMonth: "वैशाख", + paksha: "शुक्ल पक्ष", + tithi: "पूर्णिमा", + startYear: null, + endYear: null, + detail: "भगवान बुद्धको जन्मदिन र किरात समुदायको उभौली पर्व।", + holiday: true + }, + { + event: "गुरु पूर्णिमा", + eventType: "lunar", + lunarMonth: "आषाढ", + paksha: "शुक्ल पक्ष", + tithi: "पूर्णिमा", + startYear: null, + endYear: null, + detail: "गुरु र शिक्षकहरूलाई सम्मान गर्ने दिन।", + holiday: false + }, + { + event: "नाग पञ्चमी", + eventType: "lunar", + lunarMonth: "श्रावण", + paksha: "शुक्ल पक्ष", + tithi: "पञ्चमी", + startYear: null, + endYear: null, + detail: "सर्प देवताको पूजा गर्ने दिन।", + holiday: false + }, + { + event: "जनै पूर्णिमा / रक्षा बन्धन", + eventType: "lunar", + lunarMonth: "श्रावण", + paksha: "शुक्ल पक्ष", + tithi: "पूर्णिमा", + startYear: null, + endYear: null, + detail: "जनै बदल्ने तथा रक्षा बन्धन बांध्ने  पवित्र उत्सव।", + holiday: true + }, + { + event: "कृष्ण जन्माष्टमी", + eventType: "lunar", + lunarMonth: "श्रावण", + paksha: "कृष्ण पक्ष", + tithi: "अष्टमी", + startYear: null, + endYear: null, + detail: "भगवान कृष्णको जन्मदिन।", + holiday: true + }, + { + event: "गाईजात्रा", + eventType: "lunar", + lunarMonth: "भाद्रपद", + paksha: "कृष्ण पक्ष", + tithi: "प्रतिपदा", + startYear: null, + endYear: null, + detail: "गाईको उत्सव, हास्य र व्यङ्ग्यको मेल।", + holiday: true + }, + { + event: "कुशे औंसी", + eventType: "lunar", + lunarMonth: "भाद्रपद", + paksha: "कृष्ण पक्ष", + tithi: "अमावस्या", + startYear: null, + endYear: null, + detail: "बुवाको मुख हेर्ने दिन।", + holiday: true + }, + { + event: "हरितालिका तीज", + eventType: "lunar", + lunarMonth: "भाद्रपद", + paksha: "शुक्ल पक्ष", + tithi: "तृतीया", + startYear: null, + endYear: null, + detail: "महिलाहरूले को महान उत्सव।", + holiday: true + }, + { + event: "गणेश चतुर्थी", + eventType: "lunar", + lunarMonth: "भाद्रपद", + paksha: "शुक्ल पक्ष", + tithi: "चतुर्थी", + startYear: null, + endYear: null, + detail: "भगवान गणेशको जन्मदिन।", + holiday: true + }, + { + event: "इन्द्रजात्रा", + eventType: "lunar", + lunarMonth: "भाद्रपद", + paksha: "शुक्ल पक्ष", + tithi: "चतुर्दशी", + startYear: null, + endYear: null, + detail: "काठमाडौँको प्रमुख सडक उत्सवको मुख्य दिन।", + holiday: true + }, + { + event: "घटस्थापना", + eventType: "lunar", + lunarMonth: "आश्विन", + paksha: "शुक्ल पक्ष", + tithi: "प्रतिपदा", + startYear: null, + endYear: null, + detail: "दशैँको सुरुवात, नवरात्रिको पहिलो दिन।", + holiday: true + }, + { + event: "फूलपाती", + eventType: "lunar", + lunarMonth: "आश्विन", + paksha: "शुक्ल पक्ष", + tithi: "सप्तमी", + startYear: null, + endYear: null, + detail: "दशैँको सातौँ दिन।", + holiday: true + }, + { + event: "महाअष्टमी", + eventType: "lunar", + lunarMonth: "आश्विन", + paksha: "शुक्ल पक्ष", + tithi: "अष्टमी", + startYear: null, + endYear: null, + detail: "दशैँको आठौँ दिन।", + holiday: true + }, + { + event: "महानवमी", + eventType: "lunar", + lunarMonth: "आश्विन", + paksha: "शुक्ल पक्ष", + tithi: "नवमी", + startYear: null, + endYear: null, + detail: "दशैँको नवौं दिन।", + holiday: true + }, + { + event: "विजयादशमी (दशैं)", + eventType: "lunar", + lunarMonth: "आश्विन", + paksha: "शुक्ल पक्ष", + tithi: "दशमी", + startYear: null, + endYear: null, + detail: "दशैँको दशौँ दिन, विजयको दिन।", + holiday: true + }, + { + event: "कोजाग्रत पूर्णिमा", + eventType: "lunar", + lunarMonth: "आश्विन", + paksha: "शुक्ल पक्ष", + tithi: "पूर्णिमा", + startYear: null, + endYear: null, + detail: "दशैँको अन्त्य।", + holiday: true + }, + { + event: "काग तिहार", + eventType: "lunar", + lunarMonth: "कार्तिक", + paksha: "कृष्ण पक्ष", + tithi: "त्रयोदशी", + startYear: null, + endYear: null, + detail: "तिहारको पहिलो दिन, कागलाई समर्पित।", + holiday: true + }, + { + event: "कुकुर तिहार", + eventType: "lunar", + lunarMonth: "कार्तिक", + paksha: "कृष्ण पक्ष", + tithi: "चतुर्दशी", + startYear: null, + endYear: null, + detail: "तिहारको दोस्रो दिन, कुकुरलाई समर्पित।", + holiday: true + }, + { + event: "लक्ष्मी पूजा", + eventType: "lunar", + lunarMonth: "कार्तिक", + paksha: "कृष्ण पक्ष", + tithi: "अमावस्या", + startYear: null, + endYear: null, + detail: "तिहारको तेस्रो दिन, देवी लक्ष्मीको पूजा।", + holiday: true + }, + { + event: "गोवर्धन पूजा / म्ह पूजा", + eventType: "lunar", + lunarMonth: "कार्तिक", + paksha: "शुक्ल पक्ष", + tithi: "प्रतिपदा", + startYear: null, + endYear: null, + detail: "तिहारको चौथो दिन,  आत्म-पूजाको दिन।", + holiday: true + }, + { + event: "भाइटीका", + eventType: "lunar", + lunarMonth: "कार्तिक", + paksha: "शुक्ल पक्ष", + tithi: "द्वितीया", + startYear: null, + endYear: null, + detail: "तिहारको पाँचौँ दिन, दिदी-भाइबीचको प्रेमको दिन।", + holiday: true + }, + { + event: "छठ पर्व", + eventType: "lunar", + lunarMonth: "कार्तिक", + paksha: "शुक्ल पक्ष", + tithi: "षष्ठी", + startYear: null, + endYear: null, + detail: "सूर्य देवतालाई समर्पित उत्सव, मुख्यतया तराईमा मनाइन्छ।", + holiday: true + }, + { + event: "हरिबोधिनी एकादशी", + eventType: "lunar", + lunarMonth: "कार्तिक", + paksha: "शुक्ल पक्ष", + tithi: "एकादशी", + startYear: null, + endYear: null, + detail: "भगवान विष्णुको जागरणको दिन।", + holiday: false + }, + { + event: "विवाह पञ्चमी", + eventType: "lunar", + lunarMonth: "मार्गशीर्ष", + paksha: "शुक्ल पक्ष", + tithi: "पञ्चमी", + startYear: null, + endYear: null, + detail: "भगवान राम र सीताको विवाहको सम्झना।", + holiday: true + }, + { + event: "य:मरि पुन्हि / धान्यपुर्णिमा", + eventType: "lunar", + lunarMonth: "मार्गशीर्ष", + paksha: "शुक्ल पक्ष", + tithi: "पूर्णिमा", + startYear: null, + endYear: null, + detail: "नेवार समुदायको यमरि पुन्हि / धान्य पूर्णिमाको उत्सव।", + holiday: true + }, + { + event: "सरस्वती पूजा / वसन्तपञ्चमी", + eventType: "lunar", + lunarMonth: "माघ", + paksha: "शुक्ल पक्ष", + tithi: "पञ्चमी", + startYear: null, + endYear: null, + detail: "ज्ञान र शिक्षाको देवी सरस्वतीको पूजा।", + holiday: true + }, + { + event: "महाशिवरात्रि", + eventType: "lunar", + lunarMonth: "फाल्गुन", + paksha: "कृष्ण पक्ष", + tithi: "चतुर्दशी", + startYear: null, + endYear: null, + detail: "भगवान शिवको महान रात्रि।", + holiday: true + }, + { + event: "होली पूर्णिमा", + eventType: "lunar", + lunarMonth: "फाल्गुन", + paksha: "शुक्ल पक्ष", + tithi: "पूर्णिमा", + startYear: null, + endYear: null, + detail: "रङ्गहरूको उत्सव।", + holiday: true + } +]; + +// Gregorian recurring events. +var gregorianEvents = [ + { + event: "New Year's Day", + eventType: "gregorian", + date: "01/01", + startYear: null, + endYear: null, + detail: "Celebration of the new Gregorian year.", + holiday: true + }, + { + event: "Valentine's Day", + eventType: "gregorian", + date: "02/14", + startYear: null, + endYear: null, + detail: "International day of love. Nepali: प्रणय दिवस.", + holiday: false + }, + { + event: "International Women's Day", + eventType: "gregorian", + date: "03/08", + startYear: null, + endYear: null, + detail: "Global day celebrating womanhood. Nepali: नारी दिवस.", + holiday: true + }, + { + event: "World Health Day", + eventType: "gregorian", + date: "04/07", + startYear: null, + endYear: null, + detail: "A global health awareness day. Nepali: विश्व स्वास्थ्य दिवस.", + holiday: false + }, + { + event: "International Workers' Day", + eventType: "gregorian", + date: "05/01", + startYear: null, + endYear: null, + detail: "Also known as May Day. Nepali: अन्तर्राष्ट्रिय श्रमिक दिवस.", + holiday: true + }, + { + event: "World Environment Day", + eventType: "gregorian", + date: "06/05", + startYear: null, + endYear: null, + detail: "Day for environmental awareness and action. Nepali: विश्व वातावरण दिवस.", + holiday: false + }, + { + event: "International Day of Yoga", + eventType: "gregorian", + date: "06/21", + startYear: null, + endYear: null, + detail: "Global day to celebrate yoga. Nepali: विश्व योग दिवस.", + holiday: false + }, + { + event: "World Population Day", + eventType: "gregorian", + date: "07/11", + startYear: null, + endYear: null, + detail: "Day to raise awareness of global population issues. Nepali: विश्व जनसंख्या दिवस.", + holiday: false + }, + { + event: "International Youth Day", + eventType: "gregorian", + date: "08/12", + startYear: null, + endYear: null, + detail: "Day to celebrate young people as agents of change. Nepali: अन्तर्राष्ट्रिय युवा दिवस.", + holiday: false + }, + { + event: "International Human Rights Day", + eventType: "gregorian", + date: "12/10", + startYear: null, + endYear: null, + detail: "Commemorates the adoption of the Universal Declaration of Human Rights. Nepali: अन्तर्राष्ट्रिय मानव अधिकार दिवस.", + holiday: false + }, + { + event: "Christmas Day", + eventType: "gregorian", + date: "12/25", + startYear: null, + endYear: null, + detail: "Christian festival celebrating the birth of Jesus. Nepali: क्रिसमस-डे.", + holiday: true + } +]; + +// Bikram recurring events (based on BS month/day). +var bikramRecurringEvents = [ + { + event: "लोकतन्त्र दिवस", + eventType: "bikramRecurring", + date: "01/11", + startYear: 2063, + endYear: null, + detail: "लोकतन्त्र दिवस, हरेक वर्ष बैशाख ११ गते मनाइन्छ।", + holiday: true + }, + { + event: "गणतन्त्र दिवस", + eventType: "bikramRecurring", + date: "02/15", + startYear: 2065, + endYear: null, + detail: "गणतन्त्र दिवस, हरेक वर्ष जेठ १५ गते मनाइन्छ।", + holiday: true + }, + { + event: "राष्ट्रिय धान दिवस", + eventType: "bikramRecurring", + date: "03/15", + startYear: null, + endYear: null, + detail: "राष्ट्रिय धान दिवस, हरेक वर्ष असार १५ गते मनाइन्छ。", + holiday: false + }, + { + event: "भानु जयन्ती", + eventType: "bikramRecurring", + date: "03/29", + startYear: null, + endYear: null, + detail: "कवि भानुभक्त आचार्यको जन्म जयन्ती।", + holiday: false + }, + { + event: "संविधान दिवस", + eventType: "bikramRecurring", + date: "06/03", + startYear: 2072, + endYear: null, + detail: "संविधान दिवस, हरेक वर्ष असोज ३ गते मनाइन्छ।", + holiday: true + }, + { + event: "पृथ्वी जयन्ती / राष्ट्रिय एकता दिवस", + eventType: "bikramRecurring", + date: "09/27", + startYear: null, + endYear: null, + detail: "पृथ्वी जयन्ती / राष्ट्रिय एकता दिवस।", + holiday: true + }, + { + event: "शहीद दिवस", + eventType: "bikramRecurring", + date: "10/16", + startYear: null, + endYear: null, + detail: "शहीद दिवस।", + holiday: true + }, + { + event: "प्रजातन्त्र दिवस", + eventType: "bikramRecurring", + date: "11/07", + startYear: 2007, + endYear: 2062, + detail: "राणा शासनको अन्त्यको सम्झनामा मनाइने प्रजातन्त्र दिवस।", + holiday: true + }, + { + event: "नयाँ वर्ष", + eventType: "bikramRecurring", + date: "01/01", + startYear: null, + endYear: null, + detail: "नेपाली नयाँ वर्ष।", + holiday: true + }, + { + event: "बिस्का: जात्रा", + eventType: "bikramRecurring", + date: "01/01", + startYear: null, + endYear: null, + detail: "भक्तपुरमा मनाइने बिस्का जात्राको उत्सव।", + holiday: true + }, + { + event: "साउने सङ्क्रान्ति", + eventType: "bikramRecurring", + date: "04/01", + startYear: null, + endYear: null, + detail: "श्रावण सङ्क्रान्ति, श्रावण महिनाको पहिलो दिन।", + holiday: false + }, + { + event: "माघे संक्रान्ति", + eventType: "bikramRecurring", + date: "10/01", + startYear: null, + endYear: null, + detail: "माघे सङ्क्रान्ति, माघ महिनाको पहिलो दिन।", + holiday: true + } +]; + +// Bikram fixed events (specific BS year/month/day). +var bikramFixedEvents = [ + { + event: "भोटो जात्रा", + eventType: "bikramFixed", + date: "2082/02/18", + startYear: null, + endYear: null, + detail: "रातो मच्छिन्द्रनाथ जात्राको समापन दिन।", + holiday: true + }, + { + event: "तमु ल्होसार", + eventType: "bikramFixed", + date: "2082/09/15", + startYear: null, + endYear: null, + detail: "गुरुङ समुदायको नयाँ वर्ष।", + holiday: true + }, + { + event: "सोनाम ल्होसार", + eventType: "bikramFixed", + date: "2082/10/05", + startYear: null, + endYear: null, + detail: "तमाङ समुदायको नयाँ वर्ष।", + holiday: true + }, + { + event: "ग्याल्पो ल्होछार", + eventType: "bikramFixed", + date: "2082/11/06", + startYear: null, + endYear: null, + detail: "शेर्पा समुदायको नयाँ वर्ष।", + holiday: true + } +]; diff --git a/resources/PanchangaCalculator.js b/resources/PanchangaCalculator.js index 932a810..3b0d631 100644 --- a/resources/PanchangaCalculator.js +++ b/resources/PanchangaCalculator.js @@ -1,5 +1,3 @@ -// PanchangaCalculator.js - /** * Bikram Calculator - Hindu Astrological Calendar with panchanga * Copyright (C) 2025 Khumnath Cg @@ -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; }