1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 2020 The DragonFly Project. All rights reserved.
5 *
6 * This code is derived from software contributed to The DragonFly Project
7 * by Aaron LI <aly@aaronly.me>
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 * 3. Neither the name of The DragonFly Project nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific, prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #include <assert.h>
38 #include <err.h>
39 #include <math.h>
40 #include <stddef.h>
41
42 #include "calendar.h"
43 #include "basics.h"
44 #include "chinese.h"
45 #include "dates.h"
46 #include "days.h"
47 #include "ecclesiastical.h"
48 #include "gregorian.h"
49 #include "moon.h"
50 #include "nnames.h"
51 #include "parsedata.h"
52 #include "sun.h"
53 #include "utils.h"
54
55 static int find_days_yearly(int sday_id, int offset,
56 struct cal_day **dayp, char **edp);
57 static int find_days_moon(int sday_id, int offset,
58 struct cal_day **dayp, char **edp);
59
60 static int find_days_easter(int, struct cal_day **, char **);
61 static int find_days_paskha(int, struct cal_day **, char **);
62 static int find_days_advent(int, struct cal_day **, char **);
63 static int find_days_cny(int, struct cal_day **, char **);
64 static int find_days_cqingming(int, struct cal_day **, char **);
65 static int find_days_cjieqi(int, struct cal_day **, char **);
66 static int find_days_marequinox(int, struct cal_day **, char **);
67 static int find_days_sepequinox(int, struct cal_day **, char **);
68 static int find_days_junsolstice(int, struct cal_day **, char **);
69 static int find_days_decsolstice(int, struct cal_day **, char **);
70 static int find_days_newmoon(int, struct cal_day **, char **);
71 static int find_days_fullmoon(int, struct cal_day **, char **);
72
73 #define SPECIALDAY_INIT0 \
74 { SD_NONE, NULL, 0, NULL, 0, NULL }
75 #define SPECIALDAY_INIT(id, name, func) \
76 { (id), name, sizeof(name)-1, NULL, 0, func }
77 struct specialday specialdays[] = {
78 SPECIALDAY_INIT(SD_EASTER, "Easter", &find_days_easter),
79 SPECIALDAY_INIT(SD_PASKHA, "Paskha", &find_days_paskha),
80 SPECIALDAY_INIT(SD_ADVENT, "Advent", &find_days_advent),
81 SPECIALDAY_INIT(SD_CNY, "ChineseNewYear", &find_days_cny),
82 SPECIALDAY_INIT(SD_CQINGMING, "ChineseQingming", &find_days_cqingming),
83 SPECIALDAY_INIT(SD_CJIEQI, "ChineseJieqi", &find_days_cjieqi),
84 SPECIALDAY_INIT(SD_MAREQUINOX, "MarEquinox", &find_days_marequinox),
85 SPECIALDAY_INIT(SD_SEPEQUINOX, "SepEquinox", &find_days_sepequinox),
86 SPECIALDAY_INIT(SD_JUNSOLSTICE, "JunSolstice", &find_days_junsolstice),
87 SPECIALDAY_INIT(SD_DECSOLSTICE, "DecSolstice", &find_days_decsolstice),
88 SPECIALDAY_INIT(SD_NEWMOON, "NewMoon", &find_days_newmoon),
89 SPECIALDAY_INIT(SD_FULLMOON, "FullMoon", &find_days_fullmoon),
90 SPECIALDAY_INIT0,
91 };
92
93
94 static int
find_days_easter(int offset,struct cal_day ** dayp,char ** edp)95 find_days_easter(int offset, struct cal_day **dayp, char **edp)
96 {
97 return find_days_yearly(SD_EASTER, offset, dayp, edp);
98 }
99
100 static int
find_days_paskha(int offset,struct cal_day ** dayp,char ** edp)101 find_days_paskha(int offset, struct cal_day **dayp, char **edp)
102 {
103 return find_days_yearly(SD_PASKHA, offset, dayp, edp);
104 }
105
106 static int
find_days_advent(int offset,struct cal_day ** dayp,char ** edp)107 find_days_advent(int offset, struct cal_day **dayp, char **edp)
108 {
109 return find_days_yearly(SD_ADVENT, offset, dayp, edp);
110 }
111
112 static int
find_days_cny(int offset,struct cal_day ** dayp,char ** edp)113 find_days_cny(int offset, struct cal_day **dayp, char **edp)
114 {
115 return find_days_yearly(SD_CNY, offset, dayp, edp);
116 }
117
118 static int
find_days_cqingming(int offset,struct cal_day ** dayp,char ** edp)119 find_days_cqingming(int offset, struct cal_day **dayp, char **edp)
120 {
121 return find_days_yearly(SD_CQINGMING, offset, dayp, edp);
122 }
123
124 static int
find_days_marequinox(int offset,struct cal_day ** dayp,char ** edp)125 find_days_marequinox(int offset, struct cal_day **dayp, char **edp)
126 {
127 return find_days_yearly(SD_MAREQUINOX, offset, dayp, edp);
128 }
129
130 static int
find_days_sepequinox(int offset,struct cal_day ** dayp,char ** edp)131 find_days_sepequinox(int offset, struct cal_day **dayp, char **edp)
132 {
133 return find_days_yearly(SD_SEPEQUINOX, offset, dayp, edp);
134 }
135
136 static int
find_days_junsolstice(int offset,struct cal_day ** dayp,char ** edp)137 find_days_junsolstice(int offset, struct cal_day **dayp, char **edp)
138 {
139 return find_days_yearly(SD_JUNSOLSTICE, offset, dayp, edp);
140 }
141
142 static int
find_days_decsolstice(int offset,struct cal_day ** dayp,char ** edp)143 find_days_decsolstice(int offset, struct cal_day **dayp, char **edp)
144 {
145 return find_days_yearly(SD_DECSOLSTICE, offset, dayp, edp);
146 }
147
148 /*
149 * Find days of the yearly special day specified by $sday_id.
150 */
151 static int
find_days_yearly(int sday_id,int offset,struct cal_day ** dayp,char ** edp)152 find_days_yearly(int sday_id, int offset, struct cal_day **dayp, char **edp)
153 {
154 struct cal_day *dp;
155 struct date date;
156 double t, longitude;
157 char buf[32];
158 int rd, approx, month;
159 int year1, year2;
160 int count = 0;
161
162 year1 = gregorian_year_from_fixed(Options.day_begin);
163 year2 = gregorian_year_from_fixed(Options.day_end);
164 for (int y = year1; y <= year2; y++) {
165 t = NAN;
166
167 switch (sday_id) {
168 case SD_EASTER:
169 rd = easter(y);
170 break;
171 case SD_PASKHA:
172 rd = orthodox_easter(y);
173 break;
174 case SD_ADVENT:
175 rd = advent(y);
176 break;
177 case SD_CNY:
178 rd = chinese_new_year(y);
179 break;
180 case SD_CQINGMING:
181 rd = chinese_qingming(y);
182 break;
183 case SD_MAREQUINOX:
184 case SD_JUNSOLSTICE:
185 case SD_SEPEQUINOX:
186 case SD_DECSOLSTICE:
187 if (sday_id == SD_MAREQUINOX) {
188 month = 3;
189 longitude = 0.0;
190 } else if (sday_id == SD_JUNSOLSTICE) {
191 month = 6;
192 longitude = 90.0;
193 } else if (sday_id == SD_SEPEQUINOX) {
194 month = 9;
195 longitude = 180.0;
196 } else {
197 month = 12;
198 longitude = 270.0;
199 }
200 date_set(&date, y, month, 1);
201 approx = fixed_from_gregorian(&date);
202 t = solar_longitude_atafter(longitude, approx);
203 t += Options.location->zone; /* to standard time */
204 rd = floor(t);
205 break;
206 default:
207 errx(1, "%s: unknown special day: %d",
208 __func__, sday_id);
209 }
210
211 if ((dp = find_rd(rd, offset)) != NULL) {
212 if (count >= CAL_MAX_REPEAT) {
213 warnx("%s: too many repeats", __func__);
214 return count;
215 }
216 if (!isnan(t)) {
217 format_time(buf, sizeof(buf), t);
218 edp[count] = xstrdup(buf);
219 }
220 dayp[count++] = dp;
221 }
222 }
223
224 return count;
225 }
226
227 /*
228 * Find days of the 24 Chinese Jiéqì (节气)
229 */
230 static int
find_days_cjieqi(int offset,struct cal_day ** dayp,char ** edp)231 find_days_cjieqi(int offset, struct cal_day **dayp, char **edp)
232 {
233 const struct chinese_jieqi *jq;
234 struct cal_day *dp;
235 struct date date;
236 char buf[32];
237 int year1, year2;
238 int rd, rd_begin, rd_end;
239 int count = 0;
240
241 year1 = gregorian_year_from_fixed(Options.day_begin);
242 year2 = gregorian_year_from_fixed(Options.day_end);
243 for (int y = year1; y <= year2; y++) {
244 date_set(&date, y, 1, 1);
245 rd_begin = fixed_from_gregorian(&date);
246 date.year++;
247 rd_end = fixed_from_gregorian(&date);
248 if (rd_end > Options.day_end)
249 rd_end = Options.day_end;
250
251 for (rd = rd_begin; rd <= rd_end; rd += 14) {
252 rd = chinese_jieqi_onafter(rd, C_JIEQI_ALL, &jq);
253 if (rd > rd_end)
254 break;
255
256 if ((dp = find_rd(rd, offset)) != NULL) {
257 if (count >= CAL_MAX_REPEAT) {
258 warnx("%s: too many repeats",
259 __func__);
260 return count;
261 }
262 snprintf(buf, sizeof(buf), "%s, %s",
263 jq->name, jq->zhname);
264 edp[count] = xstrdup(buf);
265 dayp[count++] = dp;
266 }
267 }
268 }
269
270 return count;
271 }
272
273 static int
find_days_newmoon(int offset,struct cal_day ** dayp,char ** edp)274 find_days_newmoon(int offset, struct cal_day **dayp, char **edp)
275 {
276 return find_days_moon(SD_NEWMOON, offset, dayp, edp);
277 }
278
279 static int
find_days_fullmoon(int offset,struct cal_day ** dayp,char ** edp)280 find_days_fullmoon(int offset, struct cal_day **dayp, char **edp)
281 {
282 return find_days_moon(SD_FULLMOON, offset, dayp, edp);
283 }
284
285 /*
286 * Find days of the moon events specified by $sday_id.
287 */
288 static int
find_days_moon(int sday_id,int offset,struct cal_day ** dayp,char ** edp)289 find_days_moon(int sday_id, int offset, struct cal_day **dayp, char **edp)
290 {
291 struct cal_day *dp;
292 struct date date;
293 double t, t_begin, t_end;
294 char buf[32];
295 int year1, year2;
296 int count = 0;
297
298 year1 = gregorian_year_from_fixed(Options.day_begin);
299 year2 = gregorian_year_from_fixed(Options.day_end);
300 for (int y = year1; y <= year2; y++) {
301 date_set(&date, y, 1, 1);
302 t_begin = fixed_from_gregorian(&date) - Options.location->zone;
303 date.year++;
304 t_end = fixed_from_gregorian(&date) - Options.location->zone;
305 if (t_end > Options.day_end + 1 - Options.location->zone)
306 t_end = Options.day_end + 1 - Options.location->zone;
307 /* NOTE: '+1' to include the ending day */
308
309 for (t = t_begin; t <= t_end; ) {
310 switch (sday_id) {
311 case SD_NEWMOON:
312 t = new_moon_atafter(t);
313 break;
314 case SD_FULLMOON:
315 t = lunar_phase_atafter(180, t);
316 break;
317 default:
318 errx(1, "%s: unknown moon event: %d",
319 __func__, sday_id);
320 }
321
322 if (t > t_end)
323 break;
324
325 t += Options.location->zone; /* to standard time */
326 if ((dp = find_rd(floor(t), offset)) != NULL) {
327 if (count >= CAL_MAX_REPEAT) {
328 warnx("%s: too many repeats",
329 __func__);
330 return count;
331 }
332 format_time(buf, sizeof(buf), t);
333 edp[count] = xstrdup(buf);
334 dayp[count++] = dp;
335 }
336 }
337 }
338
339 return count;
340 }
341
342 /**************************************************************************/
343
344 /*
345 * Find days of the specified year ($year), month ($month) and day ($day).
346 * If year $year < 0, then year is ignored.
347 */
348 int
find_days_ymd(int year,int month,int day,struct cal_day ** dayp,char ** edp __unused)349 find_days_ymd(int year, int month, int day,
350 struct cal_day **dayp, char **edp __unused)
351 {
352 struct cal_day *dp = NULL;
353 int count = 0;
354
355 while ((dp = loop_dates(dp)) != NULL) {
356 if (year >= 0 && year != dp->year)
357 continue;
358 if ((dp->month == month && dp->day == day) ||
359 /* day of zero means the last day of previous month */
360 (day == 0 && dp->last_dom && month == dp->month % 12 + 1)) {
361 if (count >= CAL_MAX_REPEAT) {
362 warnx("%s: too many repeats", __func__);
363 return count;
364 }
365 dayp[count++] = dp;
366 }
367 }
368
369 return count;
370 }
371
372 /*
373 * Find days of the specified day of month ($dom) of all months.
374 */
375 int
find_days_dom(int dom,struct cal_day ** dayp,char ** edp __unused)376 find_days_dom(int dom, struct cal_day **dayp, char **edp __unused)
377 {
378 struct cal_day *dp = NULL;
379 int count = 0;
380
381 while ((dp = loop_dates(dp)) != NULL) {
382 if (dp->day == dom ||
383 /* day of zero means the last day of previous month */
384 (dom == 0 && dp->last_dom)) {
385 if (count >= CAL_MAX_REPEAT) {
386 warnx("%s: too many repeats", __func__);
387 return count;
388 }
389 dayp[count++] = dp;
390 }
391 }
392
393 return count;
394 }
395
396 /*
397 * Find days of all days of the specified month ($month).
398 */
399 int
find_days_month(int month,struct cal_day ** dayp,char ** edp __unused)400 find_days_month(int month, struct cal_day **dayp, char **edp __unused)
401 {
402 struct cal_day *dp = NULL;
403 int count = 0;
404
405 while ((dp = loop_dates(dp)) != NULL) {
406 if (dp->month == month) {
407 if (count >= CAL_MAX_REPEAT) {
408 warnx("%s: too many repeats", __func__);
409 return count;
410 }
411 dayp[count++] = dp;
412 }
413 }
414
415 return count;
416 }
417
418 /*
419 * If $index == 0, find days of every day-of-week ($dow) of the specified month
420 * ($month). Otherwise, find days of the indexed day-of-week of the month.
421 * If month $month < 0, then find days in every month.
422 */
423 int
find_days_mdow(int month,int dow,int index,struct cal_day ** dayp,char ** edp __unused)424 find_days_mdow(int month, int dow, int index,
425 struct cal_day **dayp, char **edp __unused)
426 {
427 struct cal_day *dp = NULL;
428 int count = 0;
429
430 while ((dp = loop_dates(dp)) != NULL) {
431 if (month >= 0 && month != dp->month)
432 continue;
433 if (dp->dow[0] == dow) {
434 if (index != 0 &&
435 (index != dp->dow[1] && index != dp->dow[2])) {
436 /* Not the indexed day-of-week of month */
437 continue;
438 }
439 if (count >= CAL_MAX_REPEAT) {
440 warnx("%s: too many repeats", __func__);
441 return count;
442 }
443 dayp[count++] = dp;
444 }
445 }
446
447 return count;
448 }
449