日历类生辰八字九九三伏入梅出梅算法
我们在编制应用软件时会遇到要使用日历类特性的时候,特别是需显示农历日期的一些元素。
日历类要素有:阳历农历的对应显示的算法,农历二十四节气交节时刻算法,农历年月日的干支表达算法,生辰八字的算法,还有就是九九三伏入梅出梅算法。
本文提供三类算法:
一是二十四节气交节时刻算法;
二是生辰八字的算法;
三是农历九九三伏入梅出梅算法。
二十四节气交节时刻的计算较难,有精确要求的算法是天文算法,此方法太过复杂,本文介绍一个简单的算法,对于不要求精确时刻的日历,此是个替代方法。
本文提供的生辰八字算法也是最简洁的算法。
相信大多数人对于九九三伏入梅出梅的算法有些困惑。网上的介绍都偏于理论的解释,少有实际实用的计算方法。本文就为大家提供此类功能的计算方法和打印方法。
要计算九九就要先计算得冬至日期,谚云:连冬起九,冬至日是一九第一天。
计算三伏先要计算得夏至日期,还要计算得几个是庚日的日期,夏至后第三个庚日起10天为初伏。
计算入梅出梅要计算得农历的纪日干支,因农历的依据是干支纪年法,再按 ”芒种后丙日入梅,小暑后未日出梅 ” 规则来确定入梅和出梅日期。
下例功能函数 print_bzjjff ( ){ ...... } 是九九三伏入梅出梅计算和打印的方法。
完整的万年历程序制作可参阅我在本站的博文“简明万年历编制(C语言)”。该文中有农历纪年算法和公历农历对照万年历软件界面输出方法。
下面是简单的 C 语言源码:
//******************************************
//* 日历类:节气九九三伏入梅出梅算法
//******************************************
Canvas cs; //画布,显示日历
string sBarDes[10];
int nBarId[10];
float src[4]; //ClearDraw (cls) clear screen
int ds[3]; //get date yymmdd
int dy,dm,dd;
int wd; //weekday
int alln,alln1; //总天数
int n,n0,n1 ;
string s,ss,s0 ;
string s1,s2,s3,s4;
string wds,wdss; //weekday
double dy0,dy1,dm1,dd1; //日期计算
string dy2,dm2,dd2 ;
double hh1,mm1,ss1; //时间计算
string hh2,mm2,ss2;
string date$ ; //日期串
string jname1,jname2; //节气计算
string jqnames;
double jd;
double juD,tht,yrD;
double sD; //shouD
double vs,dalt; //solar
int jqdn,jqd1,jqd2; //print solar day ( calendar )
string jqs1,jqs2;
string jqss1,jqss2;
string gzs,ggzs; //干支计算
int gzy,gzm,gzd,gzh; //干支 yy mm dd hh
int gzn1;
int yz,mz; //年柱 月柱
string bzs; //八字串
int dzd0,dzd1; //冬至日
string p1, p2, p3, p4, p5 ;
string getss ; //return $
main(){
setTitle ("日历类算法 (二)" );
jqnames="小寒大寒立春雨水惊蛰春分清明谷雨立夏小满芒种夏至小暑大暑立秋处暑白露秋分寒露霜降立冬小雪大雪冬至";
wdss="星期日星期一星期二星期三星期四星期五星期六";
gzs="甲子乙丑丙寅丁卯戊辰己巳庚午辛未壬申癸酉甲戌乙亥丙子丁丑戊寅己卯庚辰辛己壬午癸未甲申乙酉丙戌丁亥戊子己丑庚寅辛卯壬辰癸巳甲午乙未丙申丁酉戊戌己亥庚子辛丑壬寅癸卯甲辰乙巳丙午丁未戊申己酉庚戌辛亥壬子癸丑甲寅乙卯丙辰丁巳戊午己未庚申辛酉壬戌癸亥";
setDisplay (0); //不显示画布
sBarDes[0]="输入日期";
nBarId[0]=100;
sBarDes[1]=" 计 算 ";
nBarId[1]=101;
sBarDes[2]="< 上 月 ";
nBarId[2]=102;
sBarDes[3]="下 月 > ";
nBarId[3]=103;
sBarDes[4]="退出程序";
nBarId[4]=104;
sBarDes[5]=" ";
nBarId[5]=105;
setToolBarHeight(10);
setButtonTextSize(13);
setToolBarBackgroundColor(255,250,250,250);
setButtonColor(255,240,240,240);
setButtonTextColor(255,0,0,220);
setToolBar(100,myToolBarProc,sBarDes,nBarId,6);
getDate(ds); //getDate = today
dy=ds[0] ; dm=ds[1] ; dd=ds[2] ;
cal_bzjjff ();
while (){}
}//main ()
inputDate (){
//** 日期输入
getDate(ds); //getDate = today
pickDate("输入日期:",ds); //input date
clearOutput ();
dy=ds[0];
dm=ds[1];
dd=ds[2];
print "InputDate = ",dy ,"-",dm,"-",dd;
dd2=intToString(dd); //this day $
cal_bzjjff (); //计算
}//inputDate()
cal_bzjjff (){
//** 计算生辰八字,节气,九九,三伏,入梅出梅
print "InputDate = ",dy ,"-",dm,"-",dd ;
getWeekday () ; //计算总天数,星期几
print " ";
//** 计算当前月节气中气
//**** calculate solarterm
dy2=intToString(dy);
dm2=intToString(dm);
dy1=stringToDouble(dy2);
dm1=stringToDouble(dm2);
cs.SetTextSize (30);
dy0=dy1;
n=dm*2-1;
caljq(); //节气计算
jqd1=(int)(dd1);
jqs1=subString(jname2,0,2);
cs.DrawText (jname2,40,790);
jqss1=jname2;
n=dm*2;
caljq (); //节气计算
jqd2=(int)(dd1);
jqs2=subString(jname2,0,2);
cs.DrawText(jname2,40,830);
jqss2=jname2;
//** 计算冬至日 >>> 计算九九
if (dm==12)dzd1=jqd2; //show 九九
print "本年冬至 >>> ", dzd1;
if (dm<4){
n=24; //节气序号
dy0=dy1-1;
caljq ();
dzd0=(int)(dd1);
print "上年冬至 >>> ", dzd0; }
//** 计算当前日生辰八字
//** n=(dy+2096),计算至公元前(dy 是负数)
//calculate GZ,gzy,gzm,gzd,gzh, alln
//* jqd1是当月节气日,是八字年柱月柱
alln1=alln1+dd-1; //all days
n=(dy+2096);
if(dm<2)n=n-1;
if(dm==2&&dd<jqd1)n=n-1;
gzy=n-(int)(n/60*60); //mod (n, 60)
s1=subString(gzs,gzy*2,2);
if(dd<jqd1){mz=1;}else{mz=0;}
gzn1=(dy+2096)*12+dm-mz;
gzm=gzn1-(int)(gzn1/60*60);
s2=subString (gzs,gzm*2,2);
n1=alln1+9;
gzd=n1-(int)(n1/60*60);
s3=subString(gzs,gzd*2,2);
gzh=(gzd-(int)(gzd/5*5))*12;
s4=subString(gzs,gzh*2,2);
cs.SetColor(255,255,0,240);
cs.SetTextSize(27);
dd2=intToString(dd); //this day $
if(len(dd2)==1)dd2="0"+dd2;
s="当前日期 : "+dy2+" 年 "+dm2+" 月 "+dd2+ " 日";
s=s+" "+wds ;
date$=s;
// gzys=s1; gzms=s2; gzds=s3; gzhs=s4;
s="今日八字 : "+s1+" "+s2+" "+s3+" "+s4;
bzs=s; //八字 yy-mm-dd-hh
//** 计算 >>> 显示 >>> 九九三伏入梅出梅
print_bzjjff (); //算法功能函数
//** 输出计算结果
print "______________________________________ ";
print " ";
print date$;
print bzs;
print " ";
print "节气 >>> " , jqss1 ;
print "节气 >>> " , jqss2 ;
print " ";
print "计算输出:九九三伏入梅出梅 ";
print "显示 >>> "+getss;
print " ";
}//cal_bzjjff ()
getWeekday (){
//** 计算星期几
int ty;
int tm;
clearOutput ();
ty=dy;
tm=dm;
if (dm<3){
ty=dy-1;
tm=dm+12; }
//first day=1 this month 月首日 dd=1
//iWeek=(d+2*m+3*(m+1)/5+y+y/4-y/100+y/400)
//mod 7 基姆拉尔森公式
alln=dd+2*tm+3*(tm+1)/5+ty+(int)(ty/4);
alln=alln-(int)(ty/100)+(int)(ty/400)+1;
wd=alln-alln/7*7; //mod (alln,7)
wds=subString(wdss,wd*3,3);
//** 计算干支纪年,计算总天数
string yds; //for gz day
string ydds;
double tms;
yds="000031059090120151181212243273304334"; ydds=subString(yds,(dm-1)*3,3);
tms=stringToDouble(ydds);
alln1=(dy-1900)*365+((dy-1900-1)/4)+(int)(tms)+1;
if ((dy-dy/4*4)==0&&dm>2)alln1=alln1+1;
print "InputDate = ",dy ,"-",dm,"-",dd ;
print "总天数 = ",alln," ",alln1;
// return weekday >>> wds $
}//getWeekday ()
print_bzjjff ( ){
//** calculate & show 九九三伏入梅出梅
//** 芒种后丙日入梅,小暑后未日出梅 //连冬起九
int jjiu,jiu1,jiu2; //print and show 九九
string jius;
jius="一二三四五六七八九";
cs.SetTextSize (26);
cs.SetFillMode (1);//0不填色,1填色
ss=" "; s=" ";
p2=" "; p4=" "; //
if (dm>11&&dd>=dzd1||dm<4){
if(dm==12&&dd>=dzd1)jjiu=dd-dzd1;
if(dm==1)jjiu=31-dzd0+dd;
if(dm==2)jjiu=31-dzd0+31+dd; if(dm==3&&dd<13)jjiu=31-dzd0+31+28+dd;
jiu2=jjiu-(int)(jjiu/9*9)+1;
jiu1=(int)(jjiu/9)+1;
print "jiu1 = ",jiu1," jiu2 = ",jiu2; //test
s0=subString (jius,jiu1-1,1);
s=intToString (jiu2);
ss=s0+"九第 "+s+" 天 ";
if(jjiu>80)ss=" ";
print jjiu;
print ss;
if (dm==3&&dd>12)ss=" ";
cs.DrawText (ss,570,390);
p2=ss ;
} //show 九九$
//** 计算入梅出梅
if (dm==6&&dd>4&&dd<17){
s0=subString (s3,0,1);
if (s0=="丙"){ ss="今日入梅"; }
else{ ss=" "; print s0 ; }
cs.DrawText (ss,583,330);
p2=ss ; }
if (dm==7&&dd>=6&&dd<18){
//** 20240706小暑出梅同日故 dd>=6
s0=subString (s3,1,1);
if (s0=="未"){ ss="今日出梅";}else{ss=" ";}
cs.DrawText (ss,583,330);
p2=ss ; }
//** 计算三伏 calculate & show dog day
//夏至后第三个庚日起10天为初伏
//夏至后第四个庚日到立秋后第一个庚日为中伏
//立秋后第一个庚日起10天为末伏
int fn;
string fu1,fu2,fu3;
fu1="初伏第";
fu2="中伏第";
fu3="末伏第";
//** 显示7月份伏日,初伏中伏,初伏7-11>>>7-20
if (dm==7&&dd>10){
n=(dd-dd/10*10)-((gzd-gzd/10*10)-6);
if (n<1) n=n+10;
if (n<11) n=n+10; //庚日
print "伏日干支 >>> 庚日 = ", n ;
n0=dd-n+1; //庚日差值
if (dd>=n&&dd<=n+9){
ss=fu1+intToString (n0)+"天"; }
if (dd>n+9){
ss=fu2+intToString (n0-10)+"天"; }
cs.DrawText (ss,578, 390);
p2=ss ; }
//** 显示8月份伏日,中伏末伏
if (dm==8){
n=(dd-dd/10*10)-((gzd-gzd/10*10)-6);
if (n<10) n=n+10;
if (n>=10) n=n-10;
print " 伏日干支 庚日 = ", n ;
n0=31-n-10+dd; //庚日差值
if (n != 9&&n != 8) {
if (dd<n+10) {ss=fu2+intToString (n0-10)+"天"; }
if (dd>=n+10&&dd<n+20){
ss=fu3+intToString (n0-30)+"天"; }
cs.DrawText (ss,578, 390);
p2=ss ; }
if (n==9||n==8) {
//** 夏至日前一天,前二天庚日,伏30天
if (dd<n) {ss=fu2+intToString (n0-10)+"天"; }
if (dd>=n&&dd<n+10) {
ss=fu3+intToString (n0-20)+"天"; }
cs.DrawText (ss,578, 390);
p2=ss ; }
} //show three fu day
getss=p2 ; //return getss $
}//print_bzjjff ()
myToolBarProc(int nBtn,int nContext) {
if(nBtn==100){//计算星期, 显示月历
inputDate () ;
}
if(nBtn==101){//显示月历
if (dy<1900) {
getDate(ds); //getDate = today
dy=ds[0] ;
dm=ds[1] ;
dd=ds[2] ; }
cal_bzjjff ();
}
if(nBtn==102){//上一月
if (dy<1900) {
getDate(ds); //getDate = today
dy=ds[0] ;
dm=ds[1] ;
dd=ds[2] ; }
dm=dm-1 ;
if (dm<1) { dy=dy-1 ; dm=12 ; }
if (dm==2&&dd>28){ dd=28 ; }
if (dd>30){ dd=30 ; }
cal_bzjjff () ;
}
if(nBtn==103){//下一月
if (dy<1900) {
getDate(ds); //getDate = today
dy=ds[0] ;
dm=ds[1] ;
dd=ds[2] ; }
dm=dm+1 ;
if (dm>12) { dy=dy+1 ; dm=1; }
if (dm==2&&dd>28){ dd=28 ; }
if (dd>30){ dd=30 ; }
cal_bzjjff () ;
}
if(nBtn==104){//退出程序
clearOutput();
cs.ClearDraw (0,src);
setDisplay (0);
exit (0); }
if(nBtn==105){//空
setDisplay (0);
}
}//Toolbar () ***********************
caljq(){
//** calculate solar ** return jname2
//** 依据曾次亮先生节气算法拟合 >>>
juD=dy0*(365.2423112-0.000000000000064
*(dy0-100)*(dy0-100)- 0.00000003047
*(dy0-100))+15.218427*n+1721050.71301;
tht=0.0003*dy0-0.372781384-0.2617913325*n;
yrD=(1.945*sin(tht)-0.01206*sin(2*tht))
*(1.048994-0.00002583 *dy0);
sD=-0.0018*sin(2.313908653*dy0
-0.439822951-3.0443*n);
//'vs = juD '** 平气
vs = (juD + yrD + sD); // '** 定气
dalt=-15+(juD-2382148)*(juD-2382148)/41048480;
//print dalt; //test
dalt = dalt/86400;
jd = vs - 0.5 - dalt-0.025; // + 0.008;
int gg;
gg=n-(int)(n/2*2);
jdtoGD (); //儒略日转公历日期
s=jqs1+" " +jname2;
//return jname2;
}//caljq() ***********************************
//calculate jd to GD ** return jname2 *****
jdtoGD(){
double a,b,c,d,e;
double F;
double allss;
F=jd-(int)(jd);
//print " ";
//print " JD = ",jd; //test
//print " 时分秒 日小数 = ",F;
a=(int)(jd+0.5);
b=a+1537;
c=(int)((b-122.1)/365.25);
d=(int)(365.25*c);
e=(int)((b-d)/30.6001);
dd1=b-d-(int)(30.6001*e);
dm1=e-1-(int)((e/14)*12);
dy1=c-4715-(int)((7+dm1)/10);
//print a," ",b," ",c," ",d," ",e;
dy2=intToString (dy);
dm2=intToString (dm);
dd2=doubleToString (dd1);
if (len(dm2)==1)dm2="0"+dm2;
if (dd1<10)dd2="0"+dd2;
dd2=subString (dd2,0,2);
//print dy2+" 年 "+dm2+" 月 "+dd2+" 日 ";
if(n==24)jqdn=(int)(dd1);
if (n==24&&dm1<3)jqdn=jqdn+1;
//日allss 的小数转为时分秒
allss=(int)((jd-a)*86400+43200.5);
//print "allss = ", allss;
hh1=(int)(allss/3600);
mm1=(int)((allss-hh1*3600)/60);
ss1=(int)(allss-hh1*3600-mm1*60);
if(ss1>=60){
ss1=ss1-60;
mm1=mm1+1;}
if(mm1>=60){
mm1=mm1-60;
hh1=hh1+1;}
//print "JD 转为 GD,计算结果:”;
hh2=doubleToString(hh1);
mm2=doubleToString(mm1);
ss2=doubleToString(ss1);
if(hh1<10){
hh2="0"+doubleToString(hh1);}
if(mm1<10){
mm2="0"+doubleToString(mm1);}
if(ss1<10){
ss2="0"+doubleToString(ss1);}
hh2=subString (hh2,0,2);
mm2=subString (mm2,0,2);
ss2=subString (ss2,0,2);
jname1=subString(jqnames,(n-1)*2,2);
//print "节气 : ",jname1;
jname2=jname1+" > "+dm2+"-"+dd2+" "+hh2+":"+mm2+":"+ss2;
//return jname2;
}//jdtoGD() **************
//**** END ****