[plasma/kdeplasma-addons] plasmacalendarplugins/alternatecalendar/provider: [Vietnamese Lunar Calendar] Fix month offset after leap month
Nate Graham <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit a2a77048ecc3d09597b8984d1273da930817de7e by Nate Graham, on behalf of Trần Nam Tuấn.
Committed on 27/07/2026 at 19:07.
Pushed by ngraham into branch 'master'.
[Vietnamese Lunar Calendar] Fix month offset after leap month
In a leap year cycle, `k_wsAfter - k_wsBefore == 13`. Every month `k` after the
leap month has a raw offset from `k_wsBefore` that is inflated by 1 (since the
leap month consumed an extra slot). The current code never corrects for this, so
all months after the leap month are shifted one too high.
M +35 -15 plasmacalendarplugins/alternatecalendar/provider/vietnamesecalendar.cpp
https://invent.kde.org/plasma/kdeplasma-addons/-/commit/a2a77048ecc3d09597b8984d1273da930817de7e
diff --git a/plasmacalendarplugins/alternatecalendar/provider/vietnamesecalendar.cpp b/plasmacalendarplugins/alternatecalendar/provider/vietnamesecalendar.cpp
index 533e129f5..2a14d29ce 100644
--- a/plasmacalendarplugins/alternatecalendar/provider/vietnamesecalendar.cpp
+++ b/plasmacalendarplugins/alternatecalendar/provider/vietnamesecalendar.cpp
@@ -47,6 +47,13 @@ constexpr double s_newMoonInterval = 29.530588853;
*/
constexpr double s_J2000NewMoon = 2451550.09766;
+/*!
+ * \brief The UTC timezone offset of Vietnam.
+ *
+ * \internal
+ */
+constexpr int s_vietnameseUtcOffset = 7;
+
struct LunarDate {
int year;
int month;
@@ -59,14 +66,24 @@ constinit QHash<int /* k-index */, double> s_winterSolsticeCache;
constinit QHash<int /* k-index */, double> s_newMoonCache;
constinit QHash<QDate, LunarDate> s_lunarDateCache;
+/*!
+ * \brief Get the local midnight UTC+7 for \a{dayIndex}.
+ *
+ * \internal
+ */
+double getLocalMidnight(qint64 dayIndex)
+{
+ return double(dayIndex) - 0.5 // shifts backward half a day from noon UTC to midnight UTC (00:00 UTC)
+ - double(s_vietnameseUtcOffset) / 24.0; // shifts backward a further 7 hours
+}
+
/*!
\brief Get the JDE for the might of \a{dayIndex}.
\internal
*/
double getMidnightJDE(qint64 dayIndex)
{
- // Vietnamese is UTC+7
- double jdUtc = double(dayIndex) - 0.5 - 7.0 / 24.0;
+ double jdUtc = getLocalMidnight(dayIndex);
int y, m, d;
SolarUtils::getDateFromJulianDay(jdUtc, y, m, d);
@@ -86,7 +103,7 @@ qint64 getLocalDayIndex(double jde)
double dt = SolarUtils::getDeltaT(year, month) / 86400.0;
double jdUtc = jde - dt;
// Vietnamese is UTC+7
- return std::floor(jdUtc + 7.0 / 24.0 + 0.5);
+ return std::floor(jdUtc + double(s_vietnameseUtcOffset) / 24.0 + 0.5);
}
/*!
@@ -219,18 +236,17 @@ bool hasMajorSolarTerm(double jdeStart, double jdeEnd)
}
/*!
- \brief Find the lunar month index containing the given Julian date \a{jde}.
+ \brief Find the lunar month index containing the given Julian day \a{dayIndex}.
\internal
*/
-int getLocalMonthIndex(double jde)
+int getLocalMonthIndex(qint64 dayIndex)
{
- int k = std::floor((jde - s_J2000NewMoon) / s_newMoonInterval);
+ int k = std::floor((getLocalMidnight(dayIndex) - s_J2000NewMoon) / s_newMoonInterval);
// Align k perfectly so it points to the month block the day belongs to.
- const auto targetDay = getLocalDayIndex(jde);
- while (getLocalDayIndex(getNewMoonByIndex(k + 1)) <= targetDay) {
+ while (getLocalDayIndex(getNewMoonByIndex(k + 1)) <= dayIndex) {
k++;
}
- while (getLocalDayIndex(getNewMoonByIndex(k)) > targetDay) {
+ while (getLocalDayIndex(getNewMoonByIndex(k)) > dayIndex) {
k--;
}
return k;
@@ -238,6 +254,7 @@ int getLocalMonthIndex(double jde)
/*!
\brief Get the lunar date of \a{date}.
+ \internal
*/
LunarDate getLunarDate(const QDate &date)
{
@@ -253,10 +270,9 @@ LunarDate getLunarDate(const QDate &date)
int year = date.year();
auto jd = SolarUtils::toJulianDay(year, month, day);
- auto jde = double(jd);
// 1. Get the month index for the target date.
- auto k = getLocalMonthIndex(jde);
+ auto k = getLocalMonthIndex(jd);
// 2. Locate the framing Winter Solstices.
int k_wsBefore, k_wsAfter, lunarYear;
@@ -275,9 +291,10 @@ LunarDate getLunarDate(const QDate &date)
// 3. Assess if a Leap Month exists in this Solstice-to-Solstice cycle
bool isLeapMonth = false;
- if (k_wsAfter - k_wsBefore == 13) {
- int k_leap = std::numeric_limits<int>::min();
- for (int i = k_wsBefore + 1; i <= k_wsAfter; ++i) {
+ bool isLeapYear = k_wsAfter - k_wsBefore == 13; // Leap year has 13 months
+ int k_leap = std::numeric_limits<int>::min();
+ if (isLeapYear) {
+ for (int i = k_wsBefore + 1; i < k_wsAfter; ++i) {
if (!hasMajorSolarTerm(getNewMoonByIndex(i), getNewMoonByIndex(i + 1))) {
k_leap = i; // First month without a Zhongqi becomes the leap month
break;
@@ -287,9 +304,12 @@ LunarDate getLunarDate(const QDate &date)
}
// 4. Calculate final Lunar Date
- int lunarMonth = ((k - k_wsBefore) + 10) % 12;
+ int leapOffset = (isLeapYear && k > k_leap) ? 1 : 0;
+ int lunarMonth = ((k - k_wsBefore - leapOffset) + 10) % 12;
if (!isLeapMonth) { // Leap month use the same number as the previous month.
lunarMonth++;
+ } else if (lunarMonth == 0) {
+ lunarMonth = 12; // wrap 0 to 12 for leap month 12, astronomically rare but not impossible
}
if (lunarMonth < 11) { // Month before 11 belong to the following year in this calculation.