logo科技微讯

关于农历的简单调查

作者:科技微讯
日期:2023-07-14
📝 笔记

我几乎对农历一无所知,为了在我写的微信小程序通知中心中增加农历功能,我需要对农历有一些了解,以下是我的简单调查。

GitHub 有一个叫 jjonline/calendar.js 的项目,可以用来互转农历、公历,虽然它的 npm 包每周只有几十次下载,但我看到多篇文章提及了这个项目,所以我也用它。

闰年和闰月

和公历一样,农历也有闰年,但闰年在公历和农历中的定义可以说完全不同,laocaixw/calendar 对农历闰年、闰月的解释

/*
 * 农历分大小月,大月30天,小月29天,但一年中哪个月为大月,哪个月为小月,是无规律的。
 * 农历每十年有4个闰年,但哪一年为闰年也是不确定的。
 * 而闰月中,哪个闰月为大月,哪个为小月也是不确定的。
 */

我的理解是:

  • 农历的闰月是一个额外增加的月份,例如农历 2020 年是一个闰年,它增加了一个新的月份,叫润四月,位于四月之后。即四月之后不是五月,而是润四月,之后才是五月。农历 2020 年的润四月有 29 天,其他闰年可能有 30 天的。
  • 新历的闰年是指该年的 2 月份有 29 天,仅此而已。比如新历 2020 年是闰年,这一年的 2 月有 29 天,比非闰年的 2 月多了一天。

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,转换后的结果如下。

其中 IMonthCnIDayCn 两个字段分别可以用 toChinaMonthtoChinaDay 两个方法获取,不过 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 一样。

donation赞赏
thumbsup0
thumbsdown0
暂无评论