我几乎对农历一无所知,为了在我写的微信小程序通知中心中增加农历功能,我需要对农历有一些了解,以下是我的简单调查。
GitHub 有一个叫 jjonline/calendar.js 的项目,可以用来互转农历、公历,虽然它的 npm 包每周只有几十次下载,但我看到多篇文章提及了这个项目,所以我也用它。
和公历一样,农历也有闰年,但闰年在公历和农历中的定义可以说完全不同,laocaixw/calendar 对农历闰年、闰月的解释:
/*
* 农历分大小月,大月30天,小月29天,但一年中哪个月为大月,哪个月为小月,是无规律的。
* 农历每十年有4个闰年,但哪一年为闰年也是不确定的。
* 而闰月中,哪个闰月为大月,哪个为小月也是不确定的。
*/
我的理解是:
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 四种可能。
2023-11-06 补充:js 原生支持公历转农历:new Date().toLocaleString('zh-CN-u-ca-chinese')
。
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
方法可以用来把农历转为公历,和公转农不同的是,除了传入年月日参数之外,还要传入一个表示是否为闰月的布尔值。
之所以要这样做,前面已经解释过了,是因为一个农历日期可能对应两个不同的新历日期。
lunar2solar
返回的数据格式和 solar2lunar
一样。