算法系列的二十:计算中国农历(二)

By admin in 天文台 on 2018年11月13日

(接上篇)

       
所谓的“天文算法”,就是利用经典力学定律推导行星运转轨道,对轻易时刻的行星位置进行规范计算,从而取得某种天文景象起时的日,比如日月合朔这等同上和景便是阳光和嫦娥的地心黄经(视黄经)差为0的那瞬间。能够计算任意时刻行星位置的平等效仿理论就是叫喻为星历表,比较知名的星历表有美国国家航空航天局麾下的喷气推进实验室发布的DE系列星历表,还有瑞士天文台在DE406基础及进行之瑞士星历表等等。根据行星运行规则直接计算行星位置一般不是很便利,更何况大多数私有天文计算用不达标那基本上精确的律参数,于是天文学家在这些星历表的基本功及演绎出了多可以举行简便计算,但是又能确保得精度的行星运行理论,比较著名的来VSOP82/87太阳系行星运行理论同ELP-2000/82嫦娥运行理论,这有限套理论以精度达都充分相近DE系列星历表了。关于什么利用这点儿拟伦理进行天文历法计算,请参见“日历生成算法”系列文章的老三首《用天文方法计算二十四节气》和季篇《用天文方法计算日月合朔》,本文介绍的夏历年历推算是于既由此天文算法获得了纯正的节时间跟日月合朔时间的根基及展开的。

       
中国底法定纪时采用的凡华夏公历(格里历),因此农历年历的演绎应因公历年的周期也骨干,附上农历年之信,也就是说,年历以公历的1月1日也起始,至12月31日结束,根据农历历法推导出之夏历日期信息,附加在阳历日期信息及形成双历。通常状态下,一个公历年周期都不克完好地附和到一个农历年周期上,二者的差也未稳定,因此不在稳定的照应关系,也就是说,不有于阳历的日期到农历日期的易公式,只能冲农历的历法规则推导出农历日期与公历日期的照应关系。由农历历法规则能够,上一个公历年之冬至()所于的朔望月是高达一个农历年之十一月(冬月),所以在进展节气计算时,需要算包括上一年冬季到节气在内的二十五单节气,才会对许高达达成一个农历年的十一月及眼前阴历年之十一月。在测算和的相应的初一时,考虑到有闰月的图景,需要打上一年冬天到节气前之首先个朔日,连续计算15单朔日才能够保证覆盖两只冬到中间的一整年日,图(1)显示了2011年未曾闰月的情下朔日和冬至的关系:

天文台 1

希冀(1)没有闰月情况下朔日与冬到节气干图

祈求被达成革除数字是公历月的号子,黑色圆点代表朔日,黑色三角形代表冬到节气。图(2)显示了2012年有闰月的景象下朔日和冬至的涉及:

天文台 2

图(2)有闰月情况下朔日与冬到节气干图

 

经过计算得到能够覆盖两只冬到节气的备初一时间后,就好入手建立公历日期以及农历日期的对应关系。以图(1)所展示之2011年也条例,首先根据计算得到的15个朔日(2011年只有见面因此到内部的前14只时间)时间,建立和2011年(公历年)有关的朔望月干表:

朔日编号

合朔时间

对应公历日期

月长

月名

1

01:35:39.90

2010-12-06

29

冬月

2

17:02:34.26

2011-01-04

30

腊月

3

10:30:42.67

2011-02-03

30

正月

4

04:45:59.44

2011-03-05

29

二月

5

22:32:15.13

2011-04-03

30

三月

6

14:50:31.79

2011-05-03

30

四月

7

05:02:32.51

2011-06-02

29

五月

8

16:53:54.10

2011-07-01

30

六月

9

02:39:45.06

2011-07-31

29

七月

10

11:04:06.43

2011-08-29

29

八月

11

19:08:50.09

2011-09-27

30

九月

12

03:55:54.64

2011-10-27

29

十月

13

14:09:40.97

2011-11-25

30

冬月

14

02:06:27.05

2011-12-25

29

腊月

15

15:39:23.99

2012-01-23

30

正月

表明(2)2011年朔望月跟阳历日期关系表

 

号为1和2之点滴独朔日之间的朔望月是十一月,因为冬到节气落于这个朔望月,其它月份之月名依次类推,正月底初一就是新春。输出公历和农历双历时,以月(公历)为单位,从每月第一天开始,依次判断每一样上属哪个朔望月,确定就同龙之夏历月名,然后于就同一天跟斯朔望月的初一之间距离几上,记否农历日期。以2011年1月1日为例,这无异于天在2010年12月6日(2010年农历十一月的初一)和2011年1月4日间(2010年农历腊月底初一),查表(1)可知对应之阴历月是十一月,这无异龙同2010年12月6日距26龙,因此这无异于天之阴历日期就是“廿七”。再因2011年2月3日(春节)这同样龙呢条例,查朔望月表得知2月3日属自2月3日开始之朔望月,这个朔望月的月名是正月,而2月3日虽是月首,农历日期是初一,正月初一即使是春节。

事先来介绍两单函数,这点儿只函数分别用于计算节气以及日月合朔发生的时刻,函数算法的切切实实描述将在“日历生成算法”系列文章的老三篇《用天文方法计算二十四节气》和季首《用天文方法算日月合朔》中牵线,此处只是简单介绍一下用法。首先是算节气时间之函数:

    5 double CalculateSolarTerms(int year, int angle);

 

本条函数用于计算指定的年(year参数)中,太阳在黄道上运行(视运动)到指定角度时之时间,angle可以设定节气发生时的角度,比如CalculateSolarTerms(2011,
270)就是算2011年冬天到的工夫。这个函数返回的日子档次是儒略日,关于儒略日之认证求参见“日历生成算法”系列文章的首先篇《中国公历(格里历)》。

       
接下介绍计算日月合朔时间的函数:

    8 double CalculateMoonShuoJD(double tdJD);

 

夫函数返回指定时间附近的初一时间,搜索的范围是tdJD参数指定时间的前一天到后29.5305天,tdJD参数与返回值的时间项目且是儒略日。

       
生成指定公历年份的阳历和农历的双历年历的流水线如下:

 

天文台 3

祈求(3)计算公农历双历年历的流程

 

GetAllSolarTermsJD()函数从指定年份的指定节气开始,连续计算25只节气时间,时间得跨年份,内部判断过冬至节气后自行转至下一致年的节气继续计算:

  139 void CChineseCalendar::GetAllSolarTermsJD(int year, int start, double *SolarTerms)

  140 {

  141     int i = 0;

  142     int st = start;

  143     while(i < 25)

  144     {

  145         double jd = CalculateSolarTerms(year, st * 15);

  147         if(st == WINTER_SOLSTICE)

  148         {

  149             year++;

  150         }

  151         st = (st + 1) % SOLAR_TERMS_COUNT;

  152     }

  153 }

start参数是节的目录,定义二十四节气的目录如下:

   38 const int VERNAL_EQUINOX      = 0;    // 春分

   39 const int CLEAR_AND_BRIGHT    = 1;    // 清明

   40 const int GRAIN_RAIN          = 2;    // 谷雨

   41 const int SUMMER_BEGINS       = 3;    // 立夏

   42 const int GRAIN_BUDS          = 4;    // 小满

   43 const int GRAIN_IN_EAR        = 5;    // 芒种

   44 const int SUMMER_SOLSTICE     = 6;    // 夏至

   45 const int SLIGHT_HEAT         = 7;    // 小暑

   46 const int GREAT_HEAT          = 8;    // 大暑

   47 const int AUTUMN_BEGINS       = 9;    // 立秋

   48 const int STOPPING_THE_HEAT   = 10;   // 处暑

   49 const int WHITE_DEWS          = 11;   // 白露

   50 const int AUTUMN_EQUINOX      = 12;   // 秋分

   51 const int COLD_DEWS           = 13;   // 寒露

   52 const int HOAR_FROST_FALLS    = 14;   // 霜降

   53 const int WINTER_BEGINS       = 15;   // 立冬

   54 const int LIGHT_SNOW          = 16;   // 小雪

   55 const int HEAVY_SNOW          = 17;   // 大雪

   56 const int WINTER_SOLSTICE     = 18;   // 冬至

   57 const int SLIGHT_COLD         = 19;   // 小寒

   58 const int GREAT_COLD          = 20;   // 大寒

   59 const int SPRING_BEGINS       = 21;   // 立春

   60 const int THE_RAINS           = 22;   // 雨水

   61 const int INSECTS_AWAKEN      = 23;   // 惊蛰

 

节气索引乘以15不怕是节在黄道上相应之度数。GetNewMoonJDs()函数从指定时间初步接连计算15只朔日时间,从第一只冬到节气前的率先独朔日开始。15个朔日可以形成14单完整的朔望月,保证在闹闰月的场面下呢会包含两独冬到节气:

  137 void CChineseCalendar::GetNewMoonJDs(double jd, double *NewMoon)

  138 {

  139     for(int i = 0; i < NEW_MOON_CALC_COUNT; i++)

  140     {

  141         double shuoJD = CalculateMoonShuoJD(jd);

  142         NewMoon[i] = shuoJD;

  143 

  144         jd += 29.5; /*转到下一个最接近朔日的时间*/

  145     }

  146 }

BuildAllChnMonthInfo()函数根据15只朔日时间做14独朔望月,根据附近朔日的区间计算起农历月天数用来判定大小月,并且于“十一月”开始逐为每个朔望月命名(月建名称):

  170 bool CChineseCalendar::BuildAllChnMonthInfo()

  171 {

  172     CHN_MONTH_INFO info; //一年最多可13个农历月

  173     int i;

  174     int yuejian = 11;   //采用夏历建寅,冬至所在月份为农历11月

  175     for(i = 0; i < (NEW_MOON_CALC_COUNT – 1); i++)

  176     {

  177         info.mmonth = i;

  178         info.mname = (yuejian <= 12) ? yuejian : yuejian – 12;

  179         info.shuoJD = m_NewMoonJD[i];

  180         info.nextJD = m_NewMoonJD[i + 1];

  181         info.mdays = int(info.nextJD + 0.5) – int(info.shuoJD + 0.5);

  182         info.leap = 0;

  183 

  184         CChnMonthInfo cm(&info);

  185         m_ChnMonthInfo.push_back(cm);

  186 

  187         yuejian++;

  188     }

  189 

  190     return (m_ChnMonthInfo.size() == (NEW_MOON_CALC_COUNT – 1));

  191 }

 

CalcLeapChnMonth()函数根据节气以及初一日子判定在少数单冬到节气里的农历年是否来闰月,判断的依据就是看第十四独朔日是否以其次个冬到节气之前,如果第十四单朔日发生在次只冬到节气之前,就证明以少独冬到节气中来了十三赖朔日,需要置闰月。因为农历中十二单中气属于哪个农历月是定位的,因此置闰月的长河即是逐一判断十二个中气是否当对应的农历月吃,如果按应当属于有农历月之中气却从没收获于斯旧历月吃,则是旧历月便是闰月,需要安装闰月标志,同时调动是月之后的月名。调整农历月名的办法就是是月名减一,比如原本是八月将调整为七月,这样即使以十三单月对承诺高达了十二独月名(其中大多出去的一个农历月给取名为闰某月)。如果节气以及初一发生在同一天,CalcLeapChnMonth()函数采用的凡民间历法的平整,与现行历法一致:

  194 void CChineseCalendar::CalcLeapChnMonth()

  195 {

  196     assert(m_ChnMonthInfo.size() > 0); /*阴历月的初始化必须在这个之前*/

  197 

  198     int i;

  199

  200     if(int(m_NewMoonJD[13] + 0.5) <= int(m_SolarTermsJD[24] + 0.5)) //第13月的月末没有超过冬至,说明今年需要闰一个月

  201     {

  202         //找到第一个没有中气的月

  203         i = 1;

  204         while(i < (NEW_MOON_CALC_COUNT – 1))

  205         {

  206 

  207             /*m_NewMoonJD[i + 1]是第i农历月的下一个月的月首,本该属于第i月的中气如果比下一个月

  208               的月首还晚,或者与下个月的月首是同一天(民间历法),则说明第i月没有中气*/

  209             if(int(m_NewMoonJD[i + 1] + 0.5) <= int(m_SolarTermsJD[2 * i] + 0.5))

  210                 break;

  211             i++;

  212         }

  213         if(i < (NEW_MOON_CALC_COUNT – 1)) /*找到闰月,对后面的农历月调整月名*/

  214         {

  215             m_ChnMonthInfo[i].SetLeapMonth(true);

  216             while(i < (NEW_MOON_CALC_COUNT – 1))

  217             {

  218                 m_ChnMonthInfo[i++].ReIndexMonthName();

  219             }

  220         }

  221     }

  222 }

       
从理论及道,本文介绍的算法在精度允许的限外足以算前后几千年之农历年历,但是针对古的阴历算需要小心。首先是“平朔”和“定朔”的问题,唐代以前以的是平朔方法定月首,本文介绍的计算办法运用的凡“定朔”方法,因此计算出底年历与唐代以前的历史会无一致。另外,即凡在唐代后采取“定朔”的历法,因为古代天文观测和计量为规范限制,可能无足够规范,因此与现因故天文算法计算产生的结果也许并无雷同。所以对历史农历的算计应该以历史事实为主,天文计算为辅,当计算和历史不等同时,要依据历史数据开展校正。Calendar.exe是基于本文介绍的算法编写的日历小序,没有最好多的功效,主要是为印证算法,因为没历史数据用于修正结果,因此不支持1601年先的农历算(也就是说按照天文算法计算出来的结果也许同实际历史及之历法不符)。

天文台 4

贪图(5)演示程序的界面

 

稍知识1:民间历法与历理历法

   
新中国起以后没有宣布新的“官方农历历法”,将历法与政治分离体现了时代之开拓进取,但是由于并未
“官方历法”,也引起了有问题。比如我国现行使用的阴历历法是《时宪历》,它来自清朝顺治年间(公元1645)颁布的《顺治历》,它发生零星个不足之处:一个凡是日月合朔和节的时刻因为京城本土时间也仍,也不怕是东经116过25分割的本地时间,其节气以及初月之观单适用于中原地区。其它经度的地方,因为时间之涉,对促成日月合朔和节时间之出入导致置闰和月顺序各不相同。另一个不足之处就是日月合朔时间和节时间判定不规范,如果日月合朔时间与节时间以同一天,不管具体的时是不是出先后,一律用以此节算做新月中之节气,这样一来,如果此节是中气,就会见潜移默化至闰月之设置。历理历法针对这有限接触进展了改进,对节气时间以及日月合朔时间统一用东经120度虽东八区业内时,这样以另外时区的节气以及置闰结果还是平的,以东八区正式时为仍。对于节气时间以及日月合朔时间以同一天的情景,精确计算到经常、分、秒,只有日月合朔时间以节气时间之前,这个节才含有在次月外。历理历法从理论及讲话更切合现代天文学的规范计算,但是需要留意的凡,历理历法仍然只是有于理论及之历法,我国现在的阴历历法依然是民间历法《时宪历》或《顺治历》。

 

 

稍许知识2:通式寿星公式

“通式寿星公式”是先行者整理出来的一个用以计算每年立春日期的涉公式:

Date =
向下取整(Y * D + C) – L

内,Y是年度,D的价是0.2422,C是涉值,取决于节气以及东,对于21世纪,立春节气的C值是4.475,春分节气的C值是20.646等等;

L是闰年频繁,其计算公式为:

L =
向下取整(Y/4) – 向下取整(Y/100) + 向下取整(Y/400)

 

从而“通式寿星公式”确定2011年立春日期的历程如下:

L = int(2011/4) – int(2011/100) +
int(2011/400) = 502 – 20 + 5 = 487

 

Date = int(2011×0.2422+4.475)- 487 = 491 – 487 = 4

 

用,2011年的立春日期是2月4日。

 

 

粗知识3:计算节气以及初一的阅历公式

   
以1900年1月0日(星期日)为基准日,之后的各国一样龙同基准日的差值称为“积日”,
1900年1月1日底积日是1,以后的工夫顺序类推,则计算第y年第x个节气的积日公式是:

 

F = 365.242 *
(y – 1900) + 6.2 + 15.22 *x – 1.9 * sin(0.262 * x)

 

中间x是节的目录,0代表小寒,1象征大寒,其它节气如约顺序类推。

 

计算起1900年初始第m只朔日的公式是:

 

M = 1.6 +
29.5306 * m + 0.4 * sin(1 – 0.45058 * m)

 

 

聊知识4:平朔和定朔

   
中国农历的朔望月长度是平均29.5305上,所以农历月即产生大月30龙,小月29天的分,从先秦时期到唐代,农历历法均是采取大小月轮流交替的计设置每个农历月之气数,只有个别状态下才出现连续两个老月份之景,采用这种方式的历法就叫做“平朔”。“平朔”历法简单,但是不能够担保日月合朔发生在初一及时无异于天,有或是上月的月末一上,也发或是本月初二。南北朝时,一栽新的历法被取出来,这种历法严格遵循日月合朔为月初制订农历月,采用这种艺术的历法就叫“定朔”。“定朔”历法严格以日月合朔时间确定月初,因为嫦娥公转是椭圆轨道,速度并无是均匀,所以会来连续多独大月或连续多个小月之情,导致“定朔”历法推广遇到特别死之阻碍,直到唐代,中国历法才到家弃用“平朔”,改用“定朔”。

 

 

粗知识5:正月初一及立春节气

   
立春是二十四节气之首,所以先民间都是当“立春”这等同上过节,相当给现代之春节(中国先尽管是节为是节之事态多,比如清明、冬至等等)。1911年,孙中山领导之新民主主义革命建立了中华民国,在从历法上规范将农历正月初得为“春节”,把公历1月1日早晚为“元旦”,也不怕是“新年”。农历年从初一始没有计较,但是农历生肖年从何时开始也直接发争执,目前大部分口且看“立春”节气是旧历生肖年的初步。因为于中华太古历法中,十二生肖的精打细算和天干地支有非常酷关系,所以于“论天干地支、计算廿四节气”的状下,“立春”节气应该是新生肖的始发。对于一般老百姓的话,习惯给当正月初一是生肖年之发端,因此,正月初一和“立春”节气内出生之小朋友,在规定属于相的下就发出接触麻烦了。属天还是属蛇?这是单问题。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2018 亚洲必赢手机官网 版权所有