nrelease - fix/improve livecd
[dragonfly.git] / usr.bin / calendar / days.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }