2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 2019-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
37 * Calendrical Calculations, The Ultimate Edition (4th Edition)
38 * by Edward M. Reingold and Nachum Dershowitz
39 * 2018, Cambridge University Press
43 * Implement the Chinese calendar of the 1645 version, established in the
44 * second year of the Qīng dynasty.
46 * The winter solstice (dōngzhì; 冬至) always occurs during the eleventh
47 * month of the year. The winter-solstice-to-winter-solstice period is
50 * The leap month of a 13-month winter-solstice-to-winter-solstice period
51 * is the first month that does not contain a major solar term --- that is,
52 * the first lunar month that is wholly within a solar month.
63 #include "gregorian.h"
69 * Fixed date of the start of the Chinese calendar.
70 * Ref: Sec.(19.3), Eq.(19.15)
72 static const int epoch = -963099; /* Gregorian: -2636, February, 15 */
75 * Timezone (in fraction of days) of Beijing adopted in Chinese calendar
77 * Ref: Sec.(19.1), Eq.(19.2)
82 if (gregorian_year_from_fixed(rd) < 1929)
83 return (1397.0 / 180.0 / 24.0);
89 * The universal time (UT) of (clock) midnight at the start of the fixed date
91 * Ref: Sec.(19.1), Eq.(19.7)
94 midnight_in_china(int rd)
96 return (double)rd - chinese_zone(rd);
100 * Calculate the last Chinese major solar term (zhōngqì) in range of [1, 12]
101 * before the fixed date $rd.
102 * Ref: Sec.(19.1), Eq.(19.1)
105 current_major_solar_term(int rd)
107 double t_u = midnight_in_china(rd);
108 double lon = solar_longitude(t_u);
109 return mod1(2 + div_floor((int)lon, 30), 12);
113 * Calculate the fixed date (in China) of winter solstice on or before the
114 * given fixed date $rd.
115 * Ref: Sec.(19.1), Eq.(19.8)
118 chinese_winter_solstice_onbefore(int rd)
120 /* longitude of Sun at winter solstice */
121 const double winter = 270.0;
123 /* approximate the time of winter solstice */
124 double t = midnight_in_china(rd + 1);
125 double approx = estimate_prior_solar_longitude(winter, t);
127 /* search for the date of winter solstice */
128 int day = (int)floor(approx) - 1;
129 while (winter >= solar_longitude(midnight_in_china(day+1)))
136 * Calculate the fixed date (in China) of the first new moon on or after the
137 * given fixed date $rd.
138 * Ref: Sec.(19.2), Eq.(19.9)
141 chinese_new_moon_onafter(int rd)
143 double t = new_moon_atafter(midnight_in_china(rd));
144 double t_s = t + chinese_zone(rd); /* in standard time */
146 return (int)floor(t_s);
150 * Calculate the fixed date (in China) of the first new moon before the
151 * given fixed date $rd.
152 * Ref: Sec.(19.2), Eq.(19.10)
155 chinese_new_moon_before(int rd)
157 double t = new_moon_before(midnight_in_china(rd));
158 double t_s = t + chinese_zone(rd); /* in standard time */
160 return (int)floor(t_s);
164 * Determine whether the Chinese lunar month starting on the given fixed
165 * date $rd has no major solar term.
166 * Ref: Sec.(19.2), Eq.(19.11)
169 chinese_no_major_solar_term(int rd)
171 int rd2 = chinese_new_moon_onafter(rd + 1);
172 return (current_major_solar_term(rd) ==
173 current_major_solar_term(rd2));
177 * Recursively determine whether there is a Chinese leap month on or
178 * after the lunar month starting on fixed date $m1 and at or before
179 * the lunar month starting on fixed date $m2.
180 * Ref: Sec.(19.2), Eq.(19.12)
183 chinese_prior_leap_month(int m1, int m2)
185 int m2_prev = chinese_new_moon_before(m2);
186 return ((m2 >= m1) &&
187 (chinese_no_major_solar_term(m2) ||
188 chinese_prior_leap_month(m1, m2_prev)));
192 * Calculate the fixed date of Chinese New Year in suì containing the
194 * Ref: Sec.(19.2), Eq.(19.13)
197 chinese_new_year_in_sui(int rd)
199 /* prior and following winter solstice */
200 int s1 = chinese_winter_solstice_onbefore(rd);
201 int s2 = chinese_winter_solstice_onbefore(s1 + 370);
203 /* month after the 11th month: either 12 or leap 11 */
204 int m12 = chinese_new_moon_onafter(s1 + 1);
205 /* month after m12: either 12 (or leap 12) or 1 */
206 int m13 = chinese_new_moon_onafter(m12 + 1);
207 /* the next 11th month */
208 int m11_next = chinese_new_moon_before(s2 + 1);
210 bool leap_year = (lround((m11_next - m12) / mean_synodic_month) == 12);
211 if (leap_year && (chinese_no_major_solar_term(m12) ||
212 chinese_no_major_solar_term(m13))) {
213 /* either m12 or m13 is a leap month */
214 return chinese_new_moon_onafter(m13 + 1);
221 * Calculate the fixed date of Chinese New Year on or before the given
223 * Ref: Sec.(19.2), Eq.(19.14)
226 chinese_new_year_onbefore(int rd)
228 int newyear = chinese_new_year_in_sui(rd);
232 return chinese_new_year_in_sui(rd - 180);
236 * Calculate the fixed date of Chinese New Year in Gregorian year $year.
237 * Ref: Sec.(19.6), Eq.(19.26)
240 chinese_new_year(int year)
242 struct date date = { year, 7, 1 };
243 int july1 = fixed_from_gregorian(&date);
244 return chinese_new_year_onbefore(july1);
248 * Calculate the Chinese date (cycle, year, month, leap, day) corresponding
249 * to the fixed date $rd.
250 * Ref: Sec.(19.3), Eq.(19.16)
253 chinese_from_fixed(int rd, struct chinese_date *date)
255 /* prior and following winter solstice */
256 int s1 = chinese_winter_solstice_onbefore(rd);
257 int s2 = chinese_winter_solstice_onbefore(s1 + 370);
259 /* start of month containing the given date */
260 int m = chinese_new_moon_before(rd + 1);
261 /* start of the previous month */
262 int m_prev = chinese_new_moon_before(m);
263 /* month after the last 11th month: either 12 or leap 11 */
264 int m12 = chinese_new_moon_onafter(s1 + 1);
265 /* the next 11th month */
266 int m11_next = chinese_new_moon_before(s2 + 1);
268 bool leap_year = (lround((m11_next - m12) / mean_synodic_month) == 12);
269 int month = (int)lround((m - m12) / mean_synodic_month);
270 if (leap_year && chinese_prior_leap_month(m12, m))
272 month = mod1(month, 12);
273 bool leap_month = (leap_year &&
274 chinese_no_major_solar_term(m) &&
275 !chinese_prior_leap_month(m12, m_prev));
276 int elapsed_years = (int)floor(1.5 - month/12.0 +
277 (rd - epoch) / mean_tropical_year);
279 date->cycle = div_floor(elapsed_years - 1, 60) + 1;
280 date->year = mod1(elapsed_years, 60);
282 date->leap = leap_month;
283 date->day = rd - m + 1;
287 * Calculate the fixed date corresponding to the given Chinese date $date
288 * (cycle, year, month, leap, day).
289 * Ref: Sec.(19.3), Eq.(19.17)
292 fixed_from_chinese(const struct chinese_date *date)
294 int midyear = (int)floor(epoch + mean_tropical_year *
295 ((date->cycle - 1) * 60 + date->year - 0.5));
296 int newyear = chinese_new_year_onbefore(midyear);
298 /* new moon before the given date */
299 int newmoon = chinese_new_moon_onafter(
300 newyear + (date->month - 1) * 29);
301 struct chinese_date date2;
302 chinese_from_fixed(newmoon, &date2);
303 if (date->month != date2.month || date->leap != date2.leap) {
304 /* there was a prior leap month, so get the next month */
305 newmoon = chinese_new_moon_onafter(newmoon + 1);
308 return newmoon + date->day - 1;
312 * Calculate the fixed date of Qīngmíng (清明) in Gregorian year $g_year.
313 * Ref: Sec.(19.6), Eq.(19.28)
316 chinese_qingming(int g_year)
318 double lambda = 15.0; /* Solar longitude of Qīngmíng */
319 struct date date = { g_year, 4, 1 }; /* Qīngmíng is around April 5 */
320 int rd = fixed_from_gregorian(&date);
321 double zone = chinese_zone(rd);
322 double t = solar_longitude_atafter(lambda, rd) + zone;
323 return (int)floor(t);
326 /**************************************************************************/
328 /* celestial stem, tiāngān (天干) */
329 static const struct stem {
345 /* terrestrial branch, dìzhī (地支) */
346 static const struct branch {
350 const char *zhzodiac;
352 { "Zǐ", "子", "Rat", "鼠" },
353 { "Chǒu", "丑", "Ox", "牛" },
354 { "Yín", "寅", "Tiger", "虎" },
355 { "Mǎo", "卯", "Rabbit", "兔" },
356 { "Chén", "辰", "Dragon", "龙" },
357 { "Sì", "巳", "Snake", "蛇" },
358 { "Wǔ", "午", "Horse", "马" },
359 { "Wèi", "未", "Goat", "羊" },
360 { "Shēn", "申", "Monkey", "猴" },
361 { "Yǒu", "酉", "Rooster", "鸡" },
362 { "Xū", "戌", "Dog", "狗" },
363 { "Hài", "亥", "Pig", "猪" },
366 /* Chinese names of months and days */
367 static const char *months[] = {
368 "正", "二", "三", "四", "五", "六",
369 "七", "八", "九", "十", "冬", "腊",
371 static const char *mdays[] = {
372 "初一", "初二", "初三", "初四", "初五", "初六",
373 "初七", "初八", "初九", "初十", "十一", "十二",
374 "十三", "十四", "十五", "十六", "十七", "十八",
375 "十九", "二十", "廿一", "廿二", "廿三", "廿四",
376 "廿五", "廿六", "廿七", "廿八", "廿九", "三十",
379 /* 24 major and minor solar terms (节气) */
380 static const struct chinese_jieqi jieqis[] = {
381 { "Lìchūn", "立春", false, 315 },
382 { "Yǔshuǐ", "雨水", true, 330 },
383 { "Jīngzhé", "惊蛰", false, 345 },
384 { "Chūnfēn", "春分", true, 0 },
385 { "Qīngmíng", "清明", false, 15 },
386 { "Gǔyǔ", "谷雨", true, 30 },
387 { "Lìxià", "立夏", false, 45 },
388 { "Xiǎomǎn", "小满", true, 60 },
389 { "Mángzhòng", "芒种", false, 75 },
390 { "Xiàzhì", "夏至", true, 90 },
391 { "Xiǎoshǔ", "小暑", false, 105 },
392 { "Dàshǔ", "大暑", true, 120 },
393 { "Lìqiū", "立秋", false, 135 },
394 { "Chǔshǔ", "处暑", true, 150 },
395 { "Báilù", "白露", false, 165 },
396 { "Qiūfēn", "秋分", true, 180 },
397 { "Hánlù", "寒露", false, 195 },
398 { "Shuāngjiàng", "霜降", true, 210 },
399 { "Lìdōng", "立冬", false, 225 },
400 { "Xiǎoxuě", "小雪", true, 240 },
401 { "Dàxuě", "大雪", false, 255 },
402 { "Dōngzhì", "冬至", true, 270 },
403 { "Xiǎohán", "小寒", false, 285 },
404 { "Dàhán", "大寒", true, 300 },
408 * Calculate the fixed date (in China) of the following jiéqì on or after
409 * the given fixed date $rd, with the calculated jiéqì information stored
411 * If $type is C_JIEQI_ALL, find either major or minor jiéqì;
412 * If $type is C_JIEQI_MAJOR, find only major jiéqì;
413 * If $type is C_JIEQI_MINOR, find only minor jiéqì.
416 chinese_jieqi_onafter(int rd, int type, const struct chinese_jieqi **jieqi)
418 const struct chinese_jieqi *jq1, *jq2;
419 const double zone = chinese_zone(rd);
420 double t_u = midnight_in_china(rd);
421 double lon = solar_longitude(t_u);
423 size_t n = nitems(jieqis);
425 for (size_t i = 0; i < n; i++) {
427 jq2 = &jieqis[(i+1) % n];
428 if ((type == C_JIEQI_MINOR && jq2->is_major) ||
429 (type == C_JIEQI_MAJOR && !jq2->is_major)) {
430 jq2 = &jieqis[(i+2) % n];
433 lon1 = jq1->longitude;
434 lon2 = jq2->longitude;
438 if ((lon >= lon1 && lon < lon2) ||
439 (lon + 360 >= lon1 && lon + 360 < lon2)) {
444 t_u = solar_longitude_atafter(jq2->longitude, floor(t_u));
446 return (int)floor(t_u + zone);
451 * Format the Chinese date of the given fixed date $rd in $buf.
452 * Return the formatted string length.
455 chinese_format_date(char *buf, size_t size, int rd)
457 struct chinese_date cdate;
458 chinese_from_fixed(rd, &cdate);
459 return snprintf(buf, size, "%s%s月%s",
460 cdate.leap ? "闰" : "", months[cdate.month - 1],
461 mdays[cdate.day - 1]);
466 * Find days of the specified Chinese month ($month) and day ($day).
467 * If month $month < 0, then month is ignored (i.e., all months).
468 * (NOTE: The year $year is ignored.)
471 chinese_find_days_ymd(int year __unused, int month, int day,
472 struct cal_day **dayp, char **edp __unused)
475 struct chinese_date cdate;
476 int rd, rd_begin, rd_end;
479 rd_begin = chinese_new_moon_before(Options.day_begin);
480 rd_end = chinese_new_moon_onafter(Options.day_end);
482 while (rd <= rd_end) {
483 chinese_from_fixed(rd, &cdate);
484 if (cdate.month == month || month < 0) {
486 rd = fixed_from_chinese(&cdate);
487 if ((dp = find_rd(rd, 0)) != NULL) {
488 if (count >= CAL_MAX_REPEAT) {
489 warnx("%s: too many repeats",
497 /* go to next month */
498 rd = chinese_new_moon_onafter(rd + (day == 0 ? 2 : 1));
505 * Find days of the specified Chinese day of month ($dom) of all months.
508 chinese_find_days_dom(int dom, struct cal_day **dayp, char **edp __unused)
510 return chinese_find_days_ymd(-1, -1, dom, dayp, NULL);
515 * Print the Chinese calendar of the given date $rd and events of the year.
518 show_chinese_calendar(int rd)
520 struct chinese_date date;
521 chinese_from_fixed(rd, &date);
522 /* Ref: Sec.(19.3), Eq.(19.18) */
523 struct stem stem = STEMS[mod1(date.year, 10) - 1];
524 struct branch branch = BRANCHES[mod1(date.year, 12) - 1];
527 gregorian_from_fixed(rd, &gdate);
528 int g_year = gdate.year;
529 printf("公历 (Gregorian): %d-%02d-%02d\n",
530 gdate.year, gdate.month, gdate.day);
532 printf("农历: %s%s年 [%s], %s%s月%s\n",
533 stem.zhname, branch.zhname, branch.zhzodiac,
534 date.leap ? "闰" : "", months[date.month - 1],
535 mdays[date.day - 1]);
536 printf("Chinese calendar: year %s%s [%s], %smonth %d, day %d\n",
537 stem.name, branch.name, branch.zodiac,
538 date.leap ? "leap " : "", date.month, date.day);
540 /* the following Chinese New Year */
541 int newyear = chinese_new_year_onbefore(
542 chinese_new_year_onbefore(rd) + 30*13);
543 gregorian_from_fixed(newyear, &gdate);
544 printf("新年 (Chinese New Year): %d-%02d-%02d\n",
545 gdate.year, gdate.month, gdate.day);
547 const double zone = chinese_zone(rd);
548 const struct chinese_jieqi *jq;
550 char buf_time[32], buf_zone[32];
553 /* 1st solar term (Lìchūn) is generally around February 4 */
554 date_set(&gdate, g_year, 2, 1);
555 t_jq = (double)fixed_from_gregorian(&gdate);
557 format_zone(buf_zone, sizeof(buf_zone), zone);
559 printf("\n二十四节气 (solar terms):\n");
560 for (size_t i = 0; i < nitems(jieqis); i++) {
562 lambda = jq->longitude;
563 t_jq = solar_longitude_atafter(lambda, floor(t_jq)) + zone;
564 gregorian_from_fixed(floor(t_jq), &gdate);
565 format_time(buf_time, sizeof(buf_time), t_jq);
567 printf("%s (%-13s): %3d°, %d-%02d-%02d %s %s\n",
568 jq->zhname, jq->name, lambda,
569 gdate.year, gdate.month, gdate.day,