2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 2020 The DragonFly Project. All rights reserved.
6 * This code is derived from software contributed to The DragonFly Project
7 * by Aaron LI <aly@aaronly.me>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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
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.
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
47 #include "ecclesiastical.h"
48 #include "gregorian.h"
51 #include "parsedata.h"
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);
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 **);
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),
95 find_days_easter(int offset, struct cal_day **dayp, char **edp)
97 return find_days_yearly(SD_EASTER, offset, dayp, edp);
101 find_days_paskha(int offset, struct cal_day **dayp, char **edp)
103 return find_days_yearly(SD_PASKHA, offset, dayp, edp);
107 find_days_advent(int offset, struct cal_day **dayp, char **edp)
109 return find_days_yearly(SD_ADVENT, offset, dayp, edp);
113 find_days_cny(int offset, struct cal_day **dayp, char **edp)
115 return find_days_yearly(SD_CNY, offset, dayp, edp);
119 find_days_cqingming(int offset, struct cal_day **dayp, char **edp)
121 return find_days_yearly(SD_CQINGMING, offset, dayp, edp);
125 find_days_marequinox(int offset, struct cal_day **dayp, char **edp)
127 return find_days_yearly(SD_MAREQUINOX, offset, dayp, edp);
131 find_days_sepequinox(int offset, struct cal_day **dayp, char **edp)
133 return find_days_yearly(SD_SEPEQUINOX, offset, dayp, edp);
137 find_days_junsolstice(int offset, struct cal_day **dayp, char **edp)
139 return find_days_yearly(SD_JUNSOLSTICE, offset, dayp, edp);
143 find_days_decsolstice(int offset, struct cal_day **dayp, char **edp)
145 return find_days_yearly(SD_DECSOLSTICE, offset, dayp, edp);
149 * Find days of the yearly special day specified by $sday_id.
152 find_days_yearly(int sday_id, int offset, struct cal_day **dayp, char **edp)
158 int rd, approx, month;
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++) {
172 rd = orthodox_easter(y);
178 rd = chinese_new_year(y);
181 rd = chinese_qingming(y);
187 if (sday_id == SD_MAREQUINOX) {
190 } else if (sday_id == SD_JUNSOLSTICE) {
193 } else if (sday_id == SD_SEPEQUINOX) {
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 */
207 errx(1, "%s: unknown special day: %d",
211 if ((dp = find_rd(rd, offset)) != NULL) {
212 if (count >= CAL_MAX_REPEAT) {
213 warnx("%s: too many repeats", __func__);
217 format_time(buf, sizeof(buf), t);
218 edp[count] = xstrdup(buf);
228 * Find days of the 24 Chinese Jiéqì (节气)
231 find_days_cjieqi(int offset, struct cal_day **dayp, char **edp)
233 const struct chinese_jieqi *jq;
238 int rd, rd_begin, rd_end;
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);
247 rd_end = fixed_from_gregorian(&date);
248 if (rd_end > Options.day_end)
249 rd_end = Options.day_end;
251 for (rd = rd_begin; rd <= rd_end; rd += 14) {
252 rd = chinese_jieqi_onafter(rd, C_JIEQI_ALL, &jq);
256 if ((dp = find_rd(rd, offset)) != NULL) {
257 if (count >= CAL_MAX_REPEAT) {
258 warnx("%s: too many repeats",
262 snprintf(buf, sizeof(buf), "%s, %s",
263 jq->name, jq->zhname);
264 edp[count] = xstrdup(buf);
274 find_days_newmoon(int offset, struct cal_day **dayp, char **edp)
276 return find_days_moon(SD_NEWMOON, offset, dayp, edp);
280 find_days_fullmoon(int offset, struct cal_day **dayp, char **edp)
282 return find_days_moon(SD_FULLMOON, offset, dayp, edp);
286 * Find days of the moon events specified by $sday_id.
289 find_days_moon(int sday_id, int offset, struct cal_day **dayp, char **edp)
293 double t, t_begin, t_end;
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;
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 */
309 for (t = t_begin; t <= t_end; ) {
312 t = new_moon_atafter(t);
315 t = lunar_phase_atafter(180, t);
318 errx(1, "%s: unknown moon event: %d",
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",
332 format_time(buf, sizeof(buf), t);
333 edp[count] = xstrdup(buf);
342 /**************************************************************************/
345 * Find days of the specified year ($year), month ($month) and day ($day).
346 * If year $year < 0, then year is ignored.
349 find_days_ymd(int year, int month, int day,
350 struct cal_day **dayp, char **edp __unused)
352 struct cal_day *dp = NULL;
355 while ((dp = loop_dates(dp)) != NULL) {
356 if (year >= 0 && year != dp->year)
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__);
373 * Find days of the specified day of month ($dom) of all months.
376 find_days_dom(int dom, struct cal_day **dayp, char **edp __unused)
378 struct cal_day *dp = NULL;
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__);
397 * Find days of all days of the specified month ($month).
400 find_days_month(int month, struct cal_day **dayp, char **edp __unused)
402 struct cal_day *dp = NULL;
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__);
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.
424 find_days_mdow(int month, int dow, int index,
425 struct cal_day **dayp, char **edp __unused)
427 struct cal_day *dp = NULL;
430 while ((dp = loop_dates(dp)) != NULL) {
431 if (month >= 0 && month != dp->month)
433 if (dp->dow[0] == dow) {
435 (index != dp->dow[1] && index != dp->dow[2])) {
436 /* Not the indexed day-of-week of month */
439 if (count >= CAL_MAX_REPEAT) {
440 warnx("%s: too many repeats", __func__);