写个真的万年历

创建于 12/5/2024

能算一万多年

网上的农历,都说是万年历。万年历,顾名思义,要有一万年。是这样的吗?不是这样的,这个「万」是虚指。那些网上的农历,别说一万年,连一千年都没有。这也过于虚了。

于是,我写了个万年历,真的万年历,能算一万多年,包括公元前后(不过两万年就算不了了)。点这儿可以用,建议用电脑,在手机上排版不好。纯前端写的,完全本地运行,源码在这儿

注意

这是个非常 naive 的玩具,不要实际使用。因为它:

  • 无法对应实际历法。历朝历代历法不同,算法不同,该万年历参照国标。
  • 可能有误差。天文算法无法达到国标的精度要求,加上我直接忽视了闰秒,故在某些重要时刻接近零点时可能产生错误。我本想在可能错误时给予提示,但懒得做。
  • 统一使用北京时间(东八区,UTC+8)。也就是通通不管曾经的时区变更,如夏令时。因为国标就是这么写的。若某时间在二十世纪或更早,则该万年历显示的时间可能与手机或电脑上的不同,因为后者通常使用 Asia/Shanghai 时区。请参阅《时区的坑》。
  • 体积过大。打包了其他行星之类的无用数据,因此体积不必要地大。我懒得删掉,因为压缩完也就几十 k。

此外,我不了解天文方面的知识,故可能有理论上的错误。

想法

很久以前,我就好奇农历的规律。闰年怎么闰?有人说「十九年七闰」。大小月怎么分?毫无规律。农历是如此复杂,不像公历(格里高利历),很简单明确。

后来,我看了国家标准,才发现:农历就是没有简单的规律,因为农历是根据天文现象编的!若要编算农历,必定要先预测天文现象,那就是天文算法了。因此,农历可以延续千年而基本不变,就在于它只是个历法的框架,内在的天文算法可以变化,也可以根据实际天文观测来修正。这就导致农历大小月没有规律,闰年也没有永远正确的规律。反观公历,从儒略历到格里高利历,一下子差了十天,因为他们此前没有根据实际天文观测修正。

此外,农历是阴阳历,公历是阳历,这也是农历复杂的原因。阴就是月亮,阳就是太阳,农历同时遵循了月亮和太阳的周期,每个新月(朔)必定在每月初一(反之亦然),每个冬至必定在每年十一月。反观公历,年是符合得比农历好,但月则是有名无实,完全不符合月相变化。农历想要调和月亮和太阳,必然就复杂。

不过,如果知道了规律,农历也并不复杂,国标只用了六句话(不包含概念定义),我在此简单解释。为了调和月亮和太阳,农历有两个重要概念:朔、中气。朔就是新月,中气就是第偶数个节气。朔所在的一天是某月初一,如此就定好了月。两冬至间若有十三个月,则首个不含中气的月为闰月,这就定好了闰月。冬至所在的月为十一月,一年有十二个月(不计闰月),这就结束了。其中朔和中气的严谨定义复杂一些,在此不表(因为我也不太懂),请阅读下面的参考。

实现

预测朔和中气要靠天文算法,这我怎么会?好在有现成的库,可以当调包侠。我随便选了个 astronomy-engine,直接提供了我想要的算法,简简单单。

此外,因为涉及时区问题,我使用 luxon 处理时间。参见《时区的坑》。

参考