我几乎对农历一无所知,为了在我写的微信小程序通知中心中增加农历功能,我需要对农历有一些了解,以下是我的简单调查。
GitHub 有一个叫 jjonline/calendar.js 的项目,可以用来互转农历、公历,虽然它的 npm 包每周只有几十次下载,但我看到多篇文章提及了这个项目,所以我也用它。
和公历一样,农历也有闰年,但闰年在公历和农历中的定义可以说完全不同,Claude AI 总结了如下区别:
1、设置规则不同
公历闰月根据地球绕太阳一周的时间(回归年)设置,一般每 4 年设置一次 2 月 29 日。
农历闰月根据阴阳合历,以月亮周期为基准,通过添加闰月调整农历年长度。
2、设置频率不同
公历平均 4 年设置一次闰月。
农历平均每 3 年设置一次闰月。
3、设置月份不同
公历只在 2 月设置闰月。
农历可以在农历年的各个月份后面设置闰月。
4、名称表示不同
公历将闰月加入 2 月,称为 2 月 29 日。
农历在月份后加“闰”字表示,如正月闰、二月闰等。
5、长度不同
公历闰 2 月只有 1 天,2 月 29 日。
农历闰月长度与前一个月相同,29 天或 30 天。
我的理解是:
jjonline/calendar.js 有一个 leapDays
方法可以获取某一年闰月的天数,我们可以根据返回的天数是否为 0,来判断这一年是否有闰月。如果想获取某一年的闰月是哪个月,可以用 leapMonth
。
至于新历闰年的 2 月份有多少天,可以这样计算:
const y = 2020;
const result = (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0 ? 29 : 28;
jjonline/calendar.js 有一个 monthDays
方法可以获取农历某年某月的天数,monthDays
不能获取闰月的天数,即使这一年有闰月,因为 monthDays
接收的第二个参数是月份,只能是 1 ~ 12,而农历的闰月你无法把它赋予一个 1 ~ 12 的数字。正如前面所说,闰月的天数可以用 leapDays
获取。
monthDays
是获取农历月份的天数,而获取新历月份的天数,可以用 solarDays
,当然也可以用代码计算:
const year = 2023;
const month = 7;
const dateCountOfMonth = new Date(year, month, 0).getDate();
农历每月的天数只有 29、30 两种可能,公历就有 28、29、30、31 四种可能。
solar2lunar
方法可以用来把公历转换为农历,它接收三个数字,分别是年、月、日,对于公历 2020-06-20
,转换后的结果如下。
其中 IMonthCn
、IDayCn
两个字段分别可以用 toChinaMonth
、toChinaDay
两个方法获取,不过 toChinaMonth
不能判断一个月份是否为闰月,如果你已经知道是这闰月,只需要在 toChinaMonth
返回的结果前加一个润字即可。
{
date: '2020-6-20',
lunarDate: '2020-4-29',
festival: null,
lunarFestival: null,
//农历年月日
lYear: 2020,
lMonth: 4,
lDay: 29,
//农历年生肖
Animal: '鼠',
//农历月份大写,可以显示闰月
IMonthCn: '闰四月',
//农历日的大写
IDayCn: '廿九',
//是否为闰月
isLeap: true,
//公历年月日
cYear: 2020,
cMonth: 6,
cDay: 20,
//干支年月日
gzYear: '庚子',
gzMonth: '壬午',
gzDay: '甲午',
//其他
isToday: false,
nWeek: 6,
ncWeek: '星期六',
isTerm: false,
Term: null,
astro: '双子座'
}
值得注意的是,新历 2020-06-20
对应的农历月份是一个润月,叫润四月,但 lunarDate
字段显示的是 2020-4-29
,而新历 2020-5-21
对应的农历 lunarDate
字段也是 2020-4-29
,如下所示。区别是 IMonthCn
字段不是润四月,而是四月,isLeap
字段也变成 false
。
{
date: '2020-5-21',
lunarDate: '2020-4-29',
festival: null,
lunarFestival: null,
lYear: 2020,
lMonth: 4,
lDay: 29,
Animal: '鼠',
IMonthCn: '四月',
IDayCn: '廿九',
cYear: 2020,
cMonth: 5,
cDay: 21,
gzYear: '庚子',
gzMonth: '辛巳',
gzDay: '甲子',
isToday: false,
isLeap: false,
nWeek: 4,
ncWeek: '星期四',
isTerm: false,
Term: null,
astro: '双子座'
}
lunar2solar
方法可以用来把公历转为农历,和 solar2lunar
不同的是,除了传入年月日参数之外,还要传入一个布尔参数,用来告诉 lunar2solar
你传入的公历年月日对应农历的闰月还是非闰月。
之所以要这样做,前面已经解释过了,是因为两个不同的新历日期,转换为农历日期时,在写法上可能是相同的。
lunar2solar
返回的数据格式和 solar2lunar
一样。