原算法在月循环外的 if (offset < 0) 分支根据 isLeap 重新判断加哪个月份天数, 但闰月期间的非初一日期会因为变量切换被错算到下一个普通月。 用 jjonline/calendar.js 的权威实现替换:循环内统一 offset -= temp, 退出循环后用保留的 temp 加回,简洁且正确。 修复验证: - 2023-03-22 → 闰二月初一 ✓(之前也对) - 2023-03-23 → 闰二月初二 ✓(之前错为「三月初二」) - 2023-04-19 → 闰二月廿九 ✓(之前错为「三月廿九」) - 2025-08-22 → 闰六月廿九 ✓(之前错为「七月廿九」) 维护手册新增踩坑 #13。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+43
-36
@@ -74,62 +74,69 @@ function _monthDays(year, month) {
|
||||
|
||||
/**
|
||||
* 公历转农历
|
||||
* 算法移植自 github.com/jjonline/calendar.js(权威),与本地原版的差异在月循环退出时
|
||||
* 必须保留 temp 保存最后一次月份天数,否则闰月非初一的日期会被错误地归到下一个普通月份。
|
||||
* @param {Date} solarDate
|
||||
* @returns {{ year, month, day, isLeap, lunarText }}
|
||||
*/
|
||||
function solarToLunar(solarDate) {
|
||||
const date = new Date(solarDate.getFullYear(), solarDate.getMonth(), solarDate.getDate())
|
||||
let offset = Math.round((date - BASE_DATE) / 86400000)
|
||||
const y = solarDate.getFullYear()
|
||||
const m = solarDate.getMonth() + 1
|
||||
const d = solarDate.getDate()
|
||||
|
||||
let lunarYear, lunarMonth, lunarDay
|
||||
let offset = (Date.UTC(y, m - 1, d) - Date.UTC(1900, 0, 31)) / 86400000
|
||||
let i, leap = 0, temp = 0
|
||||
|
||||
for (i = 1900; i < 2101 && offset > 0; i++) {
|
||||
temp = _lunarYearDays(i)
|
||||
offset -= temp
|
||||
}
|
||||
if (offset < 0) {
|
||||
offset += temp
|
||||
i--
|
||||
}
|
||||
|
||||
const year = i
|
||||
leap = _leapMonth(i)
|
||||
let isLeap = false
|
||||
|
||||
for (lunarYear = 1900; lunarYear < 2100 && offset > 0; lunarYear++) {
|
||||
const days = _lunarYearDays(lunarYear)
|
||||
offset -= days
|
||||
}
|
||||
if (offset < 0) {
|
||||
offset += _lunarYearDays(--lunarYear)
|
||||
}
|
||||
|
||||
const leapM = _leapMonth(lunarYear)
|
||||
let isLeapYear = false
|
||||
|
||||
for (lunarMonth = 1; lunarMonth < 13 && offset > 0; lunarMonth++) {
|
||||
if (leapM > 0 && lunarMonth === leapM + 1 && !isLeapYear) {
|
||||
--lunarMonth
|
||||
isLeapYear = true
|
||||
const leapDays = _leapDays(lunarYear)
|
||||
offset -= leapDays
|
||||
for (i = 1; i < 13 && offset > 0; i++) {
|
||||
if (leap > 0 && i === (leap + 1) && isLeap === false) {
|
||||
--i
|
||||
isLeap = true
|
||||
temp = _leapDays(year)
|
||||
} else {
|
||||
offset -= _monthDays(lunarYear, lunarMonth)
|
||||
temp = _monthDays(year, i)
|
||||
}
|
||||
if (isLeapYear && lunarMonth === leapM + 1) isLeapYear = false
|
||||
if (isLeap === true && i === (leap + 1)) {
|
||||
isLeap = false
|
||||
}
|
||||
offset -= temp
|
||||
}
|
||||
|
||||
if (offset === 0 && leapM > 0 && lunarMonth === leapM + 1) {
|
||||
if (isLeapYear) {
|
||||
isLeapYear = false
|
||||
// 闰月下标重叠导致 offset 恰好为 0 时的取反
|
||||
if (offset === 0 && leap > 0 && i === leap + 1) {
|
||||
if (isLeap) {
|
||||
isLeap = false
|
||||
} else {
|
||||
isLeapYear = true
|
||||
--lunarMonth
|
||||
isLeap = true
|
||||
--i
|
||||
}
|
||||
}
|
||||
if (offset < 0) {
|
||||
offset += isLeapYear ? _leapDays(lunarYear) : _monthDays(lunarYear, lunarMonth)
|
||||
if (isLeapYear) isLeapYear = false
|
||||
else --lunarMonth
|
||||
offset += temp
|
||||
--i
|
||||
}
|
||||
|
||||
lunarDay = offset + 1
|
||||
isLeap = isLeapYear
|
||||
const month = i
|
||||
const day = offset + 1
|
||||
|
||||
return {
|
||||
year: lunarYear,
|
||||
month: lunarMonth,
|
||||
day: lunarDay,
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
isLeap,
|
||||
lunarText: `${isLeap ? '闰' : ''}${getLunarMonthName(lunarMonth)}月${getLunarDayName(lunarDay)}`
|
||||
lunarText: `${isLeap ? '闰' : ''}${getLunarMonthName(month)}月${getLunarDayName(day)}`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user