1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
// Encoding: UTF-8
#include "calendar.h"
#include <cstdio>
#include <ctime>
/*以下为"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=t_end;
t_end=julian.getJulianKiloYear(time+day);
}
//当t时刻太阳地心视黄经大于目标角度,节气在前,t和t_end后退24h
//该循环也是一个保险循环
while(p.sun_longitude(t)>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)<target_angle)
{
t=t_middle;
}
else
{
t_end=t_middle;
}
}
}
//链表记录节气信息
time=julian.kiloYearToTime(t,i==0?date->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_longitude<sun_longitude时,由于时刻已经在朔日附近
* 因而可以判断朔日在该时刻之后
* 反之,当moon_longitude>sun_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;
}
|