天干地支 ,300年干支纪年表,农历,陰陽曆,萬年曆,VB,天文历法

如果你是阅读文章,正常翻到这一页,说明你对天文,历法,甚至编程也充满着好奇心,这是一件值得探索的事情,看似天天生活中都会遇到,然而的确不算大众。

普通但一点也不简单

2000年才学会VB就想着在windows下编写一个简单的万年历,可以公历查农历,农历查干支,手上的资料只有一本紫金山天文台的50年万年历书本,我想试试可以在这个的基础上扩充到100年,应该很简单。因为也只是想练手code,当大概知道编制原理后,也就没继续了,有闲暇时,想起这个事情了,会看看别人怎么来code这个程序,然而随着了解过程的融会贯通,还是证明我的想法太简单了,用程序查询或转换农历干支纪年这个看似普通的事情,实质一点也不简单,涉及好多学科,包括天文,数学,物理,历史,计算机编程算法。先来看一下基础要点,有些专业术语,如果看不懂,是需要另行查询的。

WIKI上农历的摘要


古代曆法基礎
:地球繞太陽公轉,四季週期(回歸年約365.25日)。
:月球繞地球公轉,月相盈虧週期(朔望月約29.53日)。
單純陰曆(如伊斯蘭曆)或陽曆(如西曆)無法同時滿足年與月的準確週期:
陰曆:12個月約354-355日,新年隨季節漂移。
陽曆:按回歸年制定,月長與朔望月略有偏差。
華夏陰陽合曆(農曆)
陰曆部分:依朔望月定月,初一為新月,大月30日、小月29日,12個月約354日。
陽曆部分:依回歸年定二十四節氣,反映四季變化。
調和陰陽
回歸年比12朔望月多約11日。
採用「十九年七閏月法」(春秋時代起),後改為「兩冬至間月相週期」決定閏月。
規則:冬至月為十一月,無中氣之月為閏月(若兩無中氣,取冬至後第一個)。
特點
新月固定於初一,新年固定於冬春之交。
每19年,生日可能與西曆重合。
現行農曆規則
定朔:初一為日月合朔,望日為十五或十六。
置閏:兩冬至間若有13個新月,置閏於第一無中氣月。
節氣:太陽每運行15黃經度為一節氣。
年月命名:冬至月為十一月,次為十二月、正月等,正月初一為元旦。
歷史變革
時憲曆:廢「十九年七閏」,改進置閏規則。
民國後:農曆失官方地位,正月初一改稱春節,採西曆紀年,輔以干支(如辛丑年)。
干支紀年
60年一循環,結合天干(十)與地支(十二),對應生肖。
月以冬至為子月,閏月與前月干支相同。
重點
華夏農曆兼顧太陽與月球運動,通過置閏調和年月週期,實現「年合四季、月合圓缺」的陰陽和諧。

2017年中华人民共和国《农历的编算与颁行》国家标准(GB/T 33661-2017)节录


(1)以北京时间为标准时间; (2)朔日为农历月的第一个农历日; (3)包含节气冬至在内的农历月为农历十一月; (4)若从某个农历十一月开始到下一个农历十一月(不含)之 间有 13 个农历月,则需要置闰。置闰规则为:取其中最先出现的一 个不包含中气的农历月为农历闰月。 (5)农历十一月之后第 2 个(不计闰月)农历月为农历年的起 始月。

农历日期编排所依据的朔和节气时刻的计算依赖于太阳和月 亮位置,按国际基本天文学规范地球自转和参考系服务规范所规定的 模型计算……https://www.iers.org/IERS/EN/DataProducts/Conventions/conventions.html

1984年2月2日0时起到1985年2月19日24时截止的农历年为甲子年,该年也是生肖纪年的鼠年;1949年10月1日是干支纪日的基准日甲子

二十四节气名称及相应的太阳地心视黄经,冬至270,立春315,春分0,立夏45,夏至,90,立秋135,秋分180,立冬225。

25年后的今天,网络资源已经很丰富,万年历,农历,公历的转换程序随手可得,八字排盘的软件也有很多,但是,国内国外的相关网站或是相关程序,良莠不齐,算法,数据来源都很成问题,2017因为这个原因,国家颁布了农历国标,但公众可查询信息在紫金山天文台网站只有22年-25年的日历资料,没有这些可靠数据,WiKi也搞不定。

农历程序及其算法

那么多年时间里,我留意过几种可以看到源代码的优秀Code。

1,06年前后百度官网给出的1000行不到万年历查询程序,代码我放上一页了,我用了很长时间才搞明白,他为什么那么简洁有力。他用16进制包含了17位二进制农历从1883年开始到2100年,月大月小闰月的信息,实现了公历和农历的高效匹配和转换,除了历法外,有点密码学加计算机算法查表的感觉了;关于节气,他使用了24节气日期速算法,通式寿星公式。简单来说就是农历月份数据库+24节气天文历速算法。

*据说:寿星公式是一种用于计算二十四节气时间的近似公式,由中国天文学家寿星(张培瑜)提出。网络上有人说是许老师寿星天文历的算法。该公式通过数学近似的方式,能够较为准确地计算出每个节气的具体时间。寿星公式的特点是简单易用,适合在没有复杂天文算法的情况下快速计算节气时间。T=Y×365.2422+C+L,T 是节气的儒略日(Julian Day)。Y 是年份(以公元纪年为准)。C 是节气的固定偏移量。L 是闰年修正项。对于每个节气,C和L的值是不同的。

2,寿星天文历(万年历),许剑伟老师(福建莆田第十中学),2008年11月20日农历网论坛发布5.08测试版

寿星天文历已将古代真实日历推算到公元前104年(与实历相符),公元前104年以前及1960年以后的农历使用现代算法 提供精准的农历年历表,提供现代定气、定朔与古代气朔的比较……功能很强大,代码也很强大,里面有些天文知识我现在也还没怎么搞懂。我把许老师的Readme放到这页的页尾,这是因为许老师也是2000开始有了个想编万年历的想法,到2008年就捣鼓出到现在也没有超越对象的天文历,佩服ing,但为什么叫寿星天文历,只有问他本人了。

3,GitHub上的很多农历库也可以看到源代码,核心算法基本都是衍生于以上两种,天文历(包括速算法在内)不需要农历日期数据库,即时演算;查表历,需要农历日期,24节气数据库,;也有混合的,以上第一种就是混合算法,但节气速算法有偏移量,而且有些情况下偏移量还要再修正,怎么修正这是天文学科的事了,这个事还是有点复杂。

def calculate_solar_term(year, term_index):
    # 寿星公式参数,24节气立春-大寒,
    C = [4.6295, 19.4596, 34.2897, 49.1198, 63.9499, 78.7800,
         93.6101, 108.4402, 123.2703, 138.1004, 152.9305, 167.7606,
         182.5907, 197.4208, 212.2509, 227.0810, 241.9111, 256.7412,
         271.5713, 286.4014, 301.2315, 316.0616, 330.8917, 345.7218]
    
    # 计算儒略日
    T = year * 365.2422 + C[term_index]
    
    # 闰年修正
    if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
        T += 0.25
    
    # 转换为日期
    JDN = int(T)  # 儒略日数
    F = T - JDN   # 小数部分(时间)
    
    # 转换为公历日期
    A = JDN + 32044
    B = (4 * A + 3) // 146097
    C = A - (146097 * B) // 4
    D = (4 * C + 3) // 1461
    E = C - (1461 * D) // 4
    M = (5 * E + 2) // 153
    
    day = E - (153 * M + 2) // 5 + 1
    month = M + 3 if M < 10 else M - 9
    year = 100 * B + D - 4800 + (1 if M >= 10 else 0)
    
    # 返回日期
    return f"{year}-{month:02d}-{int(day):02d}"

# 示例:计算2023年的春分(索引为3)
year = 2023
term_index = 3  # 春分
date = calculate_solar_term(year, term_index)
print(f"{year}年的春分日期是:{date}")

月支

干支紀月較為複雜,據維基百科,有兩種干支紀月法。

第一種紀法

此一紀法以農曆月配干支,但不計閏月,閏月的干支取上一月的干支。紀法是把月建配上天干,六十月(即五年)循環一週。由於年天干的週期是十年,年干與月干有二對一的對應關係。例如年干為甲或己時,正月的干支必然是丙寅,二月是丁卯,其餘可以類推。下表列出月干支與年天干的關係。


年天干正月二月三月四月五月六月七月八月九月十月十一月十二月
甲、己丙寅丁卯戊辰己巳庚午辛未壬申癸酉甲戌乙亥丙子丁丑
乙、庚戊寅己卯庚辰辛巳壬午癸未甲申乙酉丙戌丁亥戊子己丑
丙、辛庚寅辛卯壬辰癸巳甲午乙未丙申丁酉戊戌己亥庚子辛丑
丁、壬壬寅癸卯甲辰乙巳丙午丁未戊申己酉庚戌辛亥壬子癸丑
戊、癸甲寅乙卯丙辰丁巳戊午己未庚申辛酉壬戌癸亥甲子乙丑

從上表可方便算出任何農曆年的月干支。例如2018年是農曆戊戌年,從上表可知年天干為戊的五月干支是戊午,所以戊戌年的五月干支是戊午。有《五虎遁月歌》歌訣以便記憶正(寅)月的月干與年干的關係: 甲己之年丙作初,乙庚之歲戊為頭,丙辛歲首從庚起,丁壬壬位順流行,若問戊癸何方法,甲寅之上好推求。

其實要算出某年的月干只需記住年干為甲時正月的月干是丙就可以了,其他月的月干可先計算其與甲年干的月數,然後用同餘算術推出其月干。這裡以戊戌年五月為例,戊是甲之後的四個天干,所以如果以甲年算起,戊年五月就是甲年的 (4×12+5) 或五十三月,戊年五月的天干是丙往下數第五十二個天干,由於mod(52, 10) = 2,即五十二除以十之餘數為二,丙以後的第二個天干是戊,所以戊戌年的五月的干支是戊午。這種紀法為明朝和清朝欽天監編的官方曆書所採用

第二種紀法

這種紀法不依農曆的月份,而是根據二十四節氣分月。子月定為由大雪至小寒前一日,丑月定為由小寒至立春前一日等等。下表列出各月對應的節氣段,近似農曆月份,和近似的公曆日期。


月地支節氣段近似農曆月份近似公曆日期
大雪至小寒前一日十一月12月7日–1月5日
小寒至立春前一日十二月1月6日–2月3日
立春至驚蟄前一日正月2月4日–3月5日
驚蟄至清明前一日二月3月6日–4月4日
清明至立夏前一日三月4月5日–5月5日
立夏至芒種前一日四月5月6日–6月5日
芒種至小暑前一日五月6月6日–7月6日
小暑至立秋前一日六月7月7日–8月7日
立秋至白露前一日七月8月8日–9月7日
白露至寒露前一日八月9月8日–10月7日
寒露至立冬前一日九月10月8日–11月6日
立冬至大雪前一日十月11月7日–12月6日

二十四節氣是根據太陽在天球上的位置而定,不考慮月相,所以是純陽曆。公曆也是純陽曆,所以以上各月的公曆日期每年最多只有一兩日之差。農曆是陰陽曆,既考慮月相也顧及太陽位置,以上各月的起始日期在農曆中可以有多至三十日的變動。

古代稱由冬至到下一個冬至的週期為歲,稱由農年正月初一到下一年正月初一的週期為年。所以歲是純陽曆的概念,而年則屬陰陽曆。一歲的週期是三百六十五或三百六十六日,平均值接近回歸年(365.242日)。一年的週期大約是三百五十四日(無閏月時)或約三百八十四日(有閏月時)。用這種節氣定月的法則,一歲有十二個月,每月大至有三十或三十一日,沒有閏月。月份配上天干,六十月(即五歲)循環一週。

哪一个万年历更准确?

讲这个问题前,要明晰一点,如果时间拉长,根本不可能存在100%准确的万年历,原因有三,第一,岁差的存在,地球在黄道运行周期有变化,立春点每年都在偏移,从2000年前的白羊座,偏移到现在的双鱼座,未来会偏移到水瓶座;第二,历史上的历法都出现过调整,尤其是年代久远的历史书记载和今天的天文推算可能就不一致,如果历史记载出现错误,这样的推算也没太多实质意义,中外都一样,纠正通假字毫无意义。所以许老师的天文历推到BC104,他也觉得没什么意义了所在了。第三,从天文角度来看,一切都在变化,都在运动,未来也是一样,今天准确的算法,未来某一时刻可能就需要调整,这也是常态,推算过远的未来,同样没有实际意义。

100年-300年,这是可以确定,相对数据可靠的区间。剩下的就要看程序是什么算法,以及数据是不是可靠来源。大多数农历程序都不会告诉你数据来源,只能尽量找有数据来源信息的程序使用。

100-200年的农历可靠数据,紫金山天文台的实体书,香港天文台的日历对照……300年左右的只能靠天文算法了,当然也有人用天文算法生成数据信息使用或是各种史料中修订。至于天文算法,国标给出了参考模型,但具体怎么算,有时间,也还要多学习。

Yuk Tung Liu 廖育棟是一个比较专业的人,他的想法比较好,而且他做了很多专业工作,他有一个faq,其中关于转换历我也是这样想的。

https://ytliu0.github.io/ChineseCalendar/faq_chinese.html

学习学习再学习,天文历法慢慢学习。虽然Visual Basic成为了过去式,但Visual Code已来。香港天文台给出了1901-2100公历农历对照数据API,200年,73140天,73闰,把VB时代没完成的Code用VsCode完成,


附录:

许剑伟老师寿星天文历的ReadMe


One Comment

發佈留言