#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include "calendar.h"
#include "basics.h"
#include "chinese.h"
#include "dates.h"
#include "gregorian.h"
#include "moon.h"
#include "sun.h"
#include "utils.h"
static const int epoch = -963099;
static double
chinese_zone(int rd)
{
if (gregorian_year_from_fixed(rd) < 1929)
return (1397.0 / 180.0 / 24.0);
else
return (8.0 / 24.0);
}
static double
midnight_in_china(int rd)
{
return (double)rd - chinese_zone(rd);
}
static int
current_major_solar_term(int rd)
{
double t_u = midnight_in_china(rd);
double lon = solar_longitude(t_u);
return mod1(2 + div_floor((int)lon, 30), 12);
}
static int
chinese_winter_solstice_onbefore(int rd)
{
const double winter = 270.0;
double t = midnight_in_china(rd + 1);
double approx = estimate_prior_solar_longitude(winter, t);
int day = (int)floor(approx) - 1;
while (winter >= solar_longitude(midnight_in_china(day+1)))
day++;
return day;
}
static int
chinese_new_moon_onafter(int rd)
{
double t = new_moon_atafter(midnight_in_china(rd));
double t_s = t + chinese_zone(rd);
return (int)floor(t_s);
}
static int
chinese_new_moon_before(int rd)
{
double t = new_moon_before(midnight_in_china(rd));
double t_s = t + chinese_zone(rd);
return (int)floor(t_s);
}
static bool
chinese_no_major_solar_term(int rd)
{
int rd2 = chinese_new_moon_onafter(rd + 1);
return (current_major_solar_term(rd) ==
current_major_solar_term(rd2));
}
static bool
chinese_prior_leap_month(int m1, int m2)
{
int m2_prev = chinese_new_moon_before(m2);
return ((m2 >= m1) &&
(chinese_no_major_solar_term(m2) ||
chinese_prior_leap_month(m1, m2_prev)));
}
static int
chinese_new_year_in_sui(int rd)
{
int s1 = chinese_winter_solstice_onbefore(rd);
int s2 = chinese_winter_solstice_onbefore(s1 + 370);
int m12 = chinese_new_moon_onafter(s1 + 1);
int m13 = chinese_new_moon_onafter(m12 + 1);
int m11_next = chinese_new_moon_before(s2 + 1);
bool leap_year = (lround((m11_next - m12) / mean_synodic_month) == 12);
if (leap_year && (chinese_no_major_solar_term(m12) ||
chinese_no_major_solar_term(m13))) {
return chinese_new_moon_onafter(m13 + 1);
} else {
return m13;
}
}
static int
chinese_new_year_onbefore(int rd)
{
int newyear = chinese_new_year_in_sui(rd);
if (rd >= newyear)
return newyear;
else
return chinese_new_year_in_sui(rd - 180);
}
int
chinese_new_year(int year)
{
struct date date = { year, 7, 1 };
int july1 = fixed_from_gregorian(&date);
return chinese_new_year_onbefore(july1);
}
void
chinese_from_fixed(int rd, struct chinese_date *date)
{
int s1 = chinese_winter_solstice_onbefore(rd);
int s2 = chinese_winter_solstice_onbefore(s1 + 370);
int m = chinese_new_moon_before(rd + 1);
int m_prev = chinese_new_moon_before(m);
int m12 = chinese_new_moon_onafter(s1 + 1);
int m11_next = chinese_new_moon_before(s2 + 1);
bool leap_year = (lround((m11_next - m12) / mean_synodic_month) == 12);
int month = (int)lround((m - m12) / mean_synodic_month);
if (leap_year && chinese_prior_leap_month(m12, m))
month--;
month = mod1(month, 12);
bool leap_month = (leap_year &&
chinese_no_major_solar_term(m) &&
!chinese_prior_leap_month(m12, m_prev));
int elapsed_years = (int)floor(1.5 - month/12.0 +
(rd - epoch) / mean_tropical_year);
date->cycle = div_floor(elapsed_years - 1, 60) + 1;
date->year = mod1(elapsed_years, 60);
date->month = month;
date->leap = leap_month;
date->day = rd - m + 1;
}
int
fixed_from_chinese(const struct chinese_date *date)
{
int midyear = (int)floor(epoch + mean_tropical_year *
((date->cycle - 1) * 60 + date->year - 0.5));
int newyear = chinese_new_year_onbefore(midyear);
int newmoon = chinese_new_moon_onafter(
newyear + (date->month - 1) * 29);
struct chinese_date date2;
chinese_from_fixed(newmoon, &date2);
if (date->month != date2.month || date->leap != date2.leap) {
newmoon = chinese_new_moon_onafter(newmoon + 1);
}
return newmoon + date->day - 1;
}
int
chinese_qingming(int g_year)
{
double lambda = 15.0;
struct date date = { g_year, 4, 1 };
int rd = fixed_from_gregorian(&date);
double zone = chinese_zone(rd);
double t = solar_longitude_atafter(lambda, rd) + zone;
return (int)floor(t);
}
static const struct stem {
const char *name;
const char *zhname;
} STEMS[] = {
{ "Jiǎ", "甲" },
{ "Yǐ", "乙" },
{ "Bǐng", "丙" },
{ "Dīng", "丁" },
{ "Wù", "戊" },
{ "Jǐ", "己" },
{ "Gēng", "庚" },
{ "Xīn", "辛" },
{ "Rén", "壬" },
{ "Guǐ", "癸" },
};
static const struct branch {
const char *name;
const char *zhname;
const char *zodiac;
const char *zhzodiac;
} BRANCHES[] = {
{ "Zǐ", "子", "Rat", "鼠" },
{ "Chǒu", "丑", "Ox", "牛" },
{ "Yín", "寅", "Tiger", "虎" },
{ "Mǎo", "卯", "Rabbit", "兔" },
{ "Chén", "辰", "Dragon", "龙" },
{ "Sì", "巳", "Snake", "蛇" },
{ "Wǔ", "午", "Horse", "马" },
{ "Wèi", "未", "Goat", "羊" },
{ "Shēn", "申", "Monkey", "猴" },
{ "Yǒu", "酉", "Rooster", "鸡" },
{ "Xū", "戌", "Dog", "狗" },
{ "Hài", "亥", "Pig", "猪" },
};
static const char *months[] = {
"正", "二", "三", "四", "五", "六",
"七", "八", "九", "十", "冬", "腊",
};
static const char *mdays[] = {
"初一", "初二", "初三", "初四", "初五", "初六",
"初七", "初八", "初九", "初十", "十一", "十二",
"十三", "十四", "十五", "十六", "十七", "十八",
"十九", "二十", "廿一", "廿二", "廿三", "廿四",
"廿五", "廿六", "廿七", "廿八", "廿九", "三十",
};
static const struct chinese_jieqi jieqis[] = {
{ "Lìchūn", "立春", false, 315 },
{ "Yǔshuǐ", "雨水", true, 330 },
{ "Jīngzhé", "惊蛰", false, 345 },
{ "Chūnfēn", "春分", true, 0 },
{ "Qīngmíng", "清明", false, 15 },
{ "Gǔyǔ", "谷雨", true, 30 },
{ "Lìxià", "立夏", false, 45 },
{ "Xiǎomǎn", "小满", true, 60 },
{ "Mángzhòng", "芒种", false, 75 },
{ "Xiàzhì", "夏至", true, 90 },
{ "Xiǎoshǔ", "小暑", false, 105 },
{ "Dàshǔ", "大暑", true, 120 },
{ "Lìqiū", "立秋", false, 135 },
{ "Chǔshǔ", "处暑", true, 150 },
{ "Báilù", "白露", false, 165 },
{ "Qiūfēn", "秋分", true, 180 },
{ "Hánlù", "寒露", false, 195 },
{ "Shuāngjiàng", "霜降", true, 210 },
{ "Lìdōng", "立冬", false, 225 },
{ "Xiǎoxuě", "小雪", true, 240 },
{ "Dàxuě", "大雪", false, 255 },
{ "Dōngzhì", "冬至", true, 270 },
{ "Xiǎohán", "小寒", false, 285 },
{ "Dàhán", "大寒", true, 300 },
};
int
chinese_jieqi_onafter(int rd, int type, const struct chinese_jieqi **jieqi)
{
const struct chinese_jieqi *jq1, *jq2;
const double zone = chinese_zone(rd);
double t_u = midnight_in_china(rd);
double lon = solar_longitude(t_u);
double lon1, lon2;
size_t n = nitems(jieqis);
for (size_t i = 0; i < n; i++) {
jq1 = &jieqis[i];
jq2 = &jieqis[(i+1) % n];
if ((type == C_JIEQI_MINOR && jq2->is_major) ||
(type == C_JIEQI_MAJOR && !jq2->is_major)) {
jq2 = &jieqis[(i+2) % n];
}
lon1 = jq1->longitude;
lon2 = jq2->longitude;
if (lon2 < lon1)
lon2 += 360;
if ((lon >= lon1 && lon < lon2) ||
(lon + 360 >= lon1 && lon + 360 < lon2)) {
break;
}
}
t_u = solar_longitude_atafter(jq2->longitude, floor(t_u));
*jieqi = jq2;
return (int)floor(t_u + zone);
}
int
chinese_format_date(char *buf, size_t size, int rd)
{
struct chinese_date cdate;
chinese_from_fixed(rd, &cdate);
return snprintf(buf, size, "%s%s月%s",
cdate.leap ? "闰" : "", months[cdate.month - 1],
mdays[cdate.day - 1]);
}
int
chinese_find_days_ymd(int year __unused, int month, int day,
struct cal_day **dayp, char **edp __unused)
{
struct cal_day *dp;
struct chinese_date cdate;
int rd, rd_begin, rd_end;
int count = 0;
rd_begin = chinese_new_moon_before(Options.day_begin);
rd_end = chinese_new_moon_onafter(Options.day_end);
rd = rd_begin;
while (rd <= rd_end) {
chinese_from_fixed(rd, &cdate);
if (cdate.month == month || month < 0) {
cdate.day = day;
rd = fixed_from_chinese(&cdate);
if ((dp = find_rd(rd, 0)) != NULL) {
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats",
__func__);
return count;
}
dayp[count++] = dp;
}
}
rd = chinese_new_moon_onafter(rd + (day == 0 ? 2 : 1));
}
return count;
}
int
chinese_find_days_dom(int dom, struct cal_day **dayp, char **edp __unused)
{
return chinese_find_days_ymd(-1, -1, dom, dayp, NULL);
}
void
show_chinese_calendar(int rd)
{
struct chinese_date date;
chinese_from_fixed(rd, &date);
struct stem stem = STEMS[mod1(date.year, 10) - 1];
struct branch branch = BRANCHES[mod1(date.year, 12) - 1];
struct date gdate;
gregorian_from_fixed(rd, &gdate);
int g_year = gdate.year;
printf("公历 (Gregorian): %d-%02d-%02d\n",
gdate.year, gdate.month, gdate.day);
printf("农历: %s%s年 [%s], %s%s月%s\n",
stem.zhname, branch.zhname, branch.zhzodiac,
date.leap ? "闰" : "", months[date.month - 1],
mdays[date.day - 1]);
printf("Chinese calendar: year %s%s [%s], %smonth %d, day %d\n",
stem.name, branch.name, branch.zodiac,
date.leap ? "leap " : "", date.month, date.day);
int newyear = chinese_new_year_onbefore(
chinese_new_year_onbefore(rd) + 30*13);
gregorian_from_fixed(newyear, &gdate);
printf("新年 (Chinese New Year): %d-%02d-%02d\n",
gdate.year, gdate.month, gdate.day);
const double zone = chinese_zone(rd);
const struct chinese_jieqi *jq;
double t_jq;
char buf_time[32], buf_zone[32];
int lambda;
date_set(&gdate, g_year, 2, 1);
t_jq = (double)fixed_from_gregorian(&gdate);
format_zone(buf_zone, sizeof(buf_zone), zone);
printf("\n二十四节气 (solar terms):\n");
for (size_t i = 0; i < nitems(jieqis); i++) {
jq = &jieqis[i];
lambda = jq->longitude;
t_jq = solar_longitude_atafter(lambda, floor(t_jq)) + zone;
gregorian_from_fixed(floor(t_jq), &gdate);
format_time(buf_time, sizeof(buf_time), t_jq);
printf("%s (%-13s): %3d°, %d-%02d-%02d %s %s\n",
jq->zhname, jq->name, lambda,
gdate.year, gdate.month, gdate.day,
buf_time, buf_zone);
}
}