// Encoding: UTF-8 #include "calendar.h" #include #include /*以下为"calendar.h规定之量*/ double pi=3.14159265358979323846; double day=86400; double delta=1e-11; char jieqi[25][10]={"冬至","小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"}; /*计算需要用的类的实例化*/ Julian julian; parameter p; List list; Date* input(char* argv) { Date* date=new Date; // 输入年份,自当年元旦起算 date->tm_year=atoi(argv)-1900; date->tm_mon=0; date->tm_mday=1; date->tm_hour=0; date->tm_min=0; date->tm_sec=0; return date; } /*计算当年二十四节气时刻,自去年冬至到本年冬至*/ void JieQi(Date* date) { //计算起始时间为去年12月19日0时,开始寻找去年冬至 time_t time=mktime(date); time-=13*day; double t,t_end,t_middle;//采用二分法计算,t与t_end为间隔24h的两个时刻 double target_angle;//目标角度 Date* date_tmp; //计算每个节气对应太阳地心黄经 for(int i=0;i<25;i++,time+=12*day) { target_angle=15*i+270; if(target_angle>=360) target_angle-=360; t=julian.getJulianKiloYear(time); t_end=julian.getJulianKiloYear(time+day); //定春分 if(target_angle==0) { /* 春分太阳地心视黄经为0,一般二分法不便于计算 * 因而限定范围为[355,360)∪(0,5],即春分前后各5度 * 当t_end时刻太阳地心视黄经在[355,360)时,证明春分在后边,t和t_end前进24h */ while(p.sun_longitude(t_end)<360&&p.sun_longitude(t_end)>355) { time+=day; t=t_end; t_end=julian.getJulianKiloYear(time+day); } /* 循环体time每次在已经计算出的上一节气基础上前进12日,理论上春分在t_end之后 * 当跳出上一while循环时理论上春分时刻正在t与t_end之间 * 为保险起见,添加这个循环 * 当t时刻太阳地心视黄经在[0,5]时,证明春分在前边,t和t_end后退24h */ while(p.sun_longitude(t)<5) { time-=day; t_end=t; t=julian.getJulianKiloYear(time); } //二分法计算春分 while(t_end-t>delta) { t_middle=(t+t_end)/2; if(p.sun_longitude(t_middle)<360&&p.sun_longitude(t_middle)>355) { t=t_middle; } else { t_end=t_middle; } } } //其他情况 else { //当t_end时刻太阳地心视黄经小于目标角度,节气在后,t和t_end前进24h while(p.sun_longitude(t_end)target_angle) { time-=day; t_end=t; t=julian.getJulianKiloYear(time); } while(t_end-t>delta) { t_middle=(t+t_end)/2; if(p.sun_longitude(t_middle)tm_year+1899:date->tm_year+1900); date_tmp=localtime(&time); list.append(date_tmp,false,true,i%2==0,i,t); } } // 用以判断朔日的函数,返回true则太阳地心黄经大于月球地心黄经,即朔日在此后 bool date_forward(double sun_longitude,double moon_longitude) { /* 判断逻辑 * 当moon_longitudesun_longitude时, * 如果moon_longitude-sun_longitude<20,即度数相差不大,可知朔日在此前 * 否则,大于20时候,度数相差过大,理论上只可能为moon_longitude已接近360,sun_longitude略微大于0 * 此时实际上是月亮应当追赶太阳,因而朔日在此后 */ return moon_longitude < sun_longitude ? true : (moon_longitude-sun_longitude >= 20); } void ShuoRi(int year) { double t,t_end,end,t_middle;//采用二分法计算,t与t_end为间隔24h的两个时刻 time_t time; Date* date_tmp=new Date; //根据zhongqi_vec的最后一项(今年冬至)计算冬至下一日的0时刻 date_tmp->tm_year=list.zhongqi_vec[12]->year-1900; date_tmp->tm_mon=list.zhongqi_vec[12]->mon-1; date_tmp->tm_mday=list.zhongqi_vec[12]->day+1; date_tmp->tm_hour=0; date_tmp->tm_min=0; date_tmp->tm_sec=0; date_tmp->tm_isdst=-1; time=mktime(date_tmp); end=julian.getJulianKiloYear(time); // 计算去年冬至时刻的time date_tmp->tm_year=list.zhongqi_vec[0]->year-1900; date_tmp->tm_mon=list.zhongqi_vec[0]->mon-1; date_tmp->tm_mday=list.zhongqi_vec[0]->day; date_tmp->tm_hour=list.zhongqi_vec[0]->hour; date_tmp->tm_min=list.zhongqi_vec[0]->min; date_tmp->tm_sec=list.zhongqi_vec[0]->sec; date_tmp->tm_isdst=-1; time=mktime(date_tmp); delete date_tmp; // 定朔日 for(t=julian.getJulianKiloYear(time);t<=end;time+=25*day) { // 每月长度为28-30天,因而每次循环前进25天 t=julian.getJulianKiloYear(time); t_end=julian.getJulianKiloYear(time+day); // 太阳在月亮之前,月亮追赶,故朔日在后,t和t_end前进24h while(date_forward(p.sun_longitude(t_end),p.moon_longitude(t_end))) { time+=day; t=t_end; t_end=julian.getJulianKiloYear(time+day); } // 太阳在月亮之后,月亮落后,故朔日在前,t和t_end后退24h(用以保险) while(!date_forward(p.sun_longitude(t),p.moon_longitude(t))) { time-=day; t_end=t; t=julian.getJulianKiloYear(time); } while(t_end-t>=delta) { t_middle=(t+t_end)/2; if(date_forward(p.sun_longitude(t_middle),p.moon_longitude(t_middle))) { t=t_middle; } else { t_end=t_middle; } } // 输出朔日信息 time=julian.kiloYearToTime(t,year-1); date_tmp=localtime(&time); list.append(date_tmp,true,false,false,-1,t); } } int main(int argc,char* argv[]) { if(argc!=2) { printf("Usage: %s year\\n\n",argv[0]); return 0; } Date* date=input(argv[1]); int year=date->tm_year+1900; double begin,end; JieQi(date); ShuoRi(year); list.lunar_sort(); // 奉旨置闰 list.Run(); // 输出 list.output(); // 释放内存,使用List的析构函数 delete date; return 0; }