如何在JavaScript中实现阳历到阴历的转换功能?
JavaScript实现阳历转阴历的方案
要实现阳历(公历)转阴历(农历),核心是利用预编排的农历数据——因为农历是阴阳合历,涉及节气、闰月的天文计算复杂度极高,直接用现成的农历编排表是最实用的落地方案。下面是具体的实现思路和代码:
核心思路
- 以某一个已知的“阳历-农历对应日期”为基准(比如1900年1月31日对应农历1900年正月初一);
- 计算目标阳历日期与基准日的天数差;
- 对照预定义的农历年份数据(包含每年的闰月信息、每个月的天数),将天数差映射为对应的农历年、月、日;
- 附加干支纪年的计算逻辑。
具体实现代码
// 天干地支、农历月日名称定义 const TIANGAN = ['甲','乙','丙','丁','戊','己','庚','辛','壬','癸']; const DIZHI = ['子','丑','寅','卯','辰','巳','午','未','申','酉','戌','亥']; const LUNAR_MONTHS = ['正月','二月','三月','四月','五月','六月','七月','八月','九月','十月','冬月','腊月']; const LUNAR_DAYS = ['初一','初二','初三','初四','初五','初六','初七','初八','初九','初十','十一','十二','十三','十四','十五','十六','十七','十八','十九','二十','廿一','廿二','廿三','廿四','廿五','廿六','廿七','廿八','廿九','三十']; // 农历核心数据:1900-1905年的闰月+各月天数(示例,实际需补充1900-2100完整数据) const LUNAR_YEAR_DATA = [ { leapMonth: 8, months: [29,30,29,30,29,30,29,30,29,30,29,30] }, // 1900年,闰八月 { leapMonth: 0, months: [30,29,30,29,30,29,30,29,30,29,30,29] }, // 1901年,无闰月 { leapMonth: 6, months: [29,30,29,30,29,30,29,30,29,30,29,30] }, // 1902年,闰六月 { leapMonth: 0, months: [30,29,30,29,30,29,30,29,30,29,30,29] }, // 1903年,无闰月 { leapMonth: 5, months: [29,30,29,30,29,30,29,30,29,30,29,30] }, // 1904年,闰五月 { leapMonth: 0, months: [30,29,30,29,30,29,30,29,30,29,30,29] } // 1905年,无闰月 ]; /** * 阳历转阴历 * @param {number} year - 阳历年(如2024) * @param {number} month - 阳历月(1-12) * @param {number} day - 阳历日(1-31) * @returns {object} 农历对象:{year, yearGanZhi, month, day} */ function solarToLunar(year, month, day) { // 基准日期:1900年1月31日 = 农历1900年正月初一 const BASE_DATE = new Date(1900, 0, 31); const targetDate = new Date(year, month - 1, day); // 计算天数差(排除时分秒影响) const diffDays = Math.floor((targetDate - BASE_DATE) / (1000 * 60 * 60 * 24)); if (diffDays < 0) throw new Error('仅支持1900年正月初一及以后的日期转换'); let totalDays = 0; let lunarYear = 1900; let lunarMonth = 1; let lunarDay = 1; let leapMonth = 0; // 定位农历年份 while (lunarYear <= 2100) { const yearData = LUNAR_YEAR_DATA[lunarYear - 1900]; if (!yearData) throw new Error('暂不支持该年份的转换,请补充农历数据'); // 计算当年总天数(含闰月) const yearTotalDays = yearData.months.reduce((sum, d) => sum + d, 0) + (yearData.leapMonth ? yearData.months[yearData.leapMonth - 1] : 0); if (totalDays + yearTotalDays > diffDays) break; totalDays += yearTotalDays; lunarYear++; } // 定位农历月份 const currentYearData = LUNAR_YEAR_DATA[lunarYear - 1900]; leapMonth = currentYearData.leapMonth; let monthIndex = 0; while (monthIndex < currentYearData.months.length) { const monthDays = currentYearData.months[monthIndex]; // 检查当前月是否容纳剩余天数 if (totalDays + monthDays > diffDays) { lunarMonth = monthIndex + 1; break; } totalDays += monthDays; // 处理闰月:如果当前是闰月月份,额外检查闰月 if (leapMonth === monthIndex + 1) { if (totalDays + monthDays > diffDays) { lunarMonth = `闰${LUNAR_MONTHS[leapMonth - 1]}`; break; } totalDays += monthDays; } monthIndex++; } // 计算农历日期 lunarDay = diffDays - totalDays + 1; // 计算干支纪年 const ganIndex = (lunarYear - 4) % 10; const zhiIndex = (lunarYear - 4) % 12; const yearGanZhi = TIANGAN[ganIndex] + DIZHI[zhiIndex]; return { year: lunarYear, yearGanZhi: yearGanZhi, month: typeof lunarMonth === 'number' ? LUNAR_MONTHS[lunarMonth - 1] : lunarMonth, day: LUNAR_DAYS[lunarDay - 1] }; } // 示例调用 console.log(solarToLunar(2024, 9, 10)); // 输出:{ year: 2024, yearGanZhi: '甲辰', month: '八月', day: '初六' }
关键说明
- 农历数据补充:示例只包含1900-1905年的数据,实际使用时需要补充完整的1900-2100年农历编排数据(网上可找到公开的完整数据集);
- 日期范围:当前代码支持1900年正月初一及以后的日期,若需扩展范围,只需补充对应年份的农历数据;
- 干支计算:干支纪年以农历年为基准,公式
(农历年-4)%10/12是因为公元4年为甲子年; - 闰月处理:代码会自动识别闰月,并在返回的月份名称前加上“闰”字。
内容的提问来源于stack exchange,提问作者Jin Liu
相关产品推荐
相关产品推荐

