图(1)没有闰月情况下朔日与冬至节气关系图
图中上排数字是公历月的编号,黑色圆点代表朔日,黑色三角形代表冬至节气。图(2)显示了2012年有闰月的情况下朔日和冬至的关系:
图(2)有闰月情况下朔日与冬至节气关系图
朔日编号
对应公历日期
月长
月名
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
正月
4
04:45:59.44
2011-03-05
二月
5
22:32:15.13
2011-04-03
三月
6
14:50:31.79
2011-05-03
四月
7
05:02:32.51
2011-06-02
五月
8
16:53:54.10
2011-07-01
六月
9
02:39:45.06
2011-07-31
七月
10
11:04:06.43
2011-08-29
八月
11
19:08:50.09
2011-09-27
九月
12
03:55:54.64
2011-10-27
十月
13
14:09:40.97
2011-11-25
14
02:06:27.05
2011-12-25
15
15:39:23.99
2012-01-23
表(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日就是月首,农历日期是初一,正月初一就是春节。
5doubleCalculateSolarTerms(intyear,intangle);
8doubleCalculateMoonShuoJD(doubletdJD);
生成指定公历年份的公历和农历的双历年历的流程如下:
图(3)计算公农历双历年历的流程
139voidCChineseCalendar::GetAllSolarTermsJD(intyear,intstart,double*SolarTerms)
140{
141inti=0;
142intst=start;
143while(i<25)
144{
145doublejd=CalculateSolarTerms(year,st*15);
147if(st==WINTER_SOLSTICE)
148{
149year++;
150}
151st=(st+1)%SOLAR_TERMS_COUNT;
152}
153}
start参数是节气的索引,定义二十四节气的索引如下:
38constintVERNAL_EQUINOX=0;//春分
39constintCLEAR_AND_BRIGHT=1;//清明
40constintGRAIN_RAIN=2;//谷雨
41constintSUMMER_BEGINS=3;//立夏
42constintGRAIN_BUDS=4;//小满
43constintGRAIN_IN_EAR=5;//芒种
44constintSUMMER_SOLSTICE=6;//夏至
45constintSLIGHT_HEAT=7;//小暑
46constintGREAT_HEAT=8;//大暑
47constintAUTUMN_BEGINS=9;//立秋
48constintSTOPPING_THE_HEAT=10;//处暑
49constintWHITE_DEWS=11;//白露
50constintAUTUMN_EQUINOX=12;//秋分
51constintCOLD_DEWS=13;//寒露
52constintHOAR_FROST_FALLS=14;//霜降
53constintWINTER_BEGINS=15;//立冬
54constintLIGHT_SNOW=16;//小雪
55constintHEAVY_SNOW=17;//大雪
56constintWINTER_SOLSTICE=18;//冬至
57constintSLIGHT_COLD=19;//小寒
58constintGREAT_COLD=20;//大寒
59constintSPRING_BEGINS=21;//立春
60constintTHE_RAINS=22;//雨水
61constintINSECTS_AWAKEN=23;//惊蛰
137voidCChineseCalendar::GetNewMoonJDs(doublejd,double*NewMoon)
138{
139for(inti=0;i 141doubleshuoJD=CalculateMoonShuoJD(jd); 142NewMoon[i]=shuoJD; 143 145} 146} 170boolCChineseCalendar::BuildAllChnMonthInfo() 171{ 172CHN_MONTH_INFOinfo;//一年最多可13个农历月 173inti; 174intyuejian=11;//采用夏历建寅,冬至所在月份为农历11月 175for(i=0;i<(NEW_MOON_CALC_COUNT-1);i++) 176{ 177info.mmonth=i; 178info.mname=(yuejian<=12)yuejian:yuejian-12; 179info.shuoJD=m_NewMoonJD[i]; 180info.nextJD=m_NewMoonJD[i+1]; 181info.mdays=int(info.nextJD+0.5)-int(info.shuoJD+0.5); 182info.leap=0; 183 184CChnMonthInfocm(&info); 185m_ChnMonthInfo.push_back(cm); 186 187yuejian++; 188} 189 190return(m_ChnMonthInfo.size()==(NEW_MOON_CALC_COUNT-1)); 191} 194voidCChineseCalendar::CalcLeapChnMonth() 195{ 196assert(m_ChnMonthInfo.size()>0);/*阴历月的初始化必须在这个之前*/ 197 198inti; 199 200if(int(m_NewMoonJD[13]+0.5)<=int(m_SolarTermsJD[24]+0.5))//第13月的月末没有超过冬至,说明今年需要闰一个月 201{ 202//找到第一个没有中气的月 203i=1; 204while(i<(NEW_MOON_CALC_COUNT-1)) 205{ 206 207/*m_NewMoonJD[i+1]是第i农历月的下一个月的月首,本该属于第i月的中气如果比下一个月 208的月首还晚,或者与下个月的月首是同一天(民间历法),则说明第i月没有中气*/ 209if(int(m_NewMoonJD[i+1]+0.5)<=int(m_SolarTermsJD[2*i]+0.5)) 210break; 211i++; 212} 213if(i<(NEW_MOON_CALC_COUNT-1))/*找到闰月,对后面的农历月调整月名*/ 214{ 215m_ChnMonthInfo[i].SetLeapMonth(true); 216while(i<(NEW_MOON_CALC_COUNT-1)) 217{ 218m_ChnMonthInfo[i++].ReIndexMonthName(); 219} 220} 221} 222} 从理论上讲,本文介绍的算法在精度允许的范围内可以计算前后几千年的农历年历,但是对古代的农历计算需要小心。首先是“平朔”和“定朔”的问题,唐代以前使用的是平朔方法定月首,本文介绍的计算方法采用的是“定朔”方法,因此计算出的年历与唐代以前的历史会不一致。另外,即是在唐代以后采用“定朔”的历法,因为古代天文观测和计算受条件限制,可能不够精确,因此与现在用天文算法计算出的结果可能并不一致。所以对历史农历的计算应该以历史事实为主,天文计算为辅,当计算与历史不一致时,要根据历史数据进行校正。Calendar.exe是根据本文介绍的算法编写的日历小程序,没有太多的功能,主要是为了验证算法,因为没有历史数据用于修正结果,因此不支持1601年以前的农历计算(也就是说按照天文算法计算出来的结果可能和实际历史上的历法不符)。 图(5)演示程序的界面 小知识1:民间历法和历理历法 小知识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:计算节气和朔日的经验公式 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:平朔和定朔 小知识5:正月初一和立春节气 立春是二十四节气之首,所以古代民间都是在“立春”这一天过节,相当于现代的春节(中国古代即是节气也是节日的情况很多,比如清明、冬至等等)。1911年,孙中山领导的辛亥革命建立了中华民国,在从历法上正式把农历正月初一定为“春节”,把公历1月1日定为“元旦”,也就是“新年”。农历年从正月初一开始没有争议,但是农历生肖年从何时开始却一直有争议,目前多数人都认为“立春”节气是农历生肖年的开始。因为在中国古代历法中,十二生肖的计算与天干地支有很大关系,所以在“论天干地支、计算廿四节气”的情况下,“立春”节气应该是新生肖的开始。对于普通老百姓来说,习惯于认为正月初一是生肖年的开始,因此,正月初一和“立春”节气之间出生的小孩,在确定属相的时候就有点麻烦了。属龙还是属蛇?这是个问题。