#include <stdbool.h>
#include <stdio.h>
#include "calendar.h"
#include "basics.h"
#include "dates.h"
#include "gregorian.h"
#include "julian.h"
#include "utils.h"
static const int epoch = -1;
bool
julian_leap_year(int year)
{
int i = (year > 0) ? 0 : 3;
return (mod(year, 4) == i);
}
int
fixed_from_julian(const struct date *date)
{
int y = (date->year >= 0) ? date->year : (date->year + 1);
int rd = ((epoch - 1) + 365 * (y - 1) +
div_floor(y - 1, 4) +
div_floor(date->month * 367 - 362, 12));
if (date->month <= 2)
return rd + date->day;
else if (julian_leap_year(date->year))
return rd + date->day - 1;
else
return rd + date->day - 2;
}
void
julian_from_fixed(int rd, struct date *date)
{
int correction, pdays;
date->year = div_floor(4 * (rd - epoch) + 1464, 1461);
if (date->year <= 0)
date->year--;
struct date d = { date->year, 3, 1 };
if (rd < fixed_from_julian(&d))
correction = 0;
else if (julian_leap_year(date->year))
correction = 1;
else
correction = 2;
d.month = 1;
pdays = rd - fixed_from_julian(&d);
date->month = div_floor(12 * (pdays + correction) + 373, 367);
d.month = date->month;
date->day = rd - fixed_from_julian(&d) + 1;
}
int
julian_format_date(char *buf, size_t size, int rd)
{
static const char *month_names[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
};
struct date jdate;
julian_from_fixed(rd, &jdate);
return snprintf(buf, size, "%s/%02d",
month_names[jdate.month - 1], jdate.day);
}
static int
julian_year_from_fixed(int rd)
{
struct date jdate;
julian_from_fixed(rd, &jdate);
return jdate.year;
}
int
julian_find_days_ymd(int year, int month, int day, struct cal_day **dayp,
char **edp __unused)
{
struct cal_day *dp;
struct date date;
int rd, year1, year2;
int count = 0;
year1 = julian_year_from_fixed(Options.day_begin);
year2 = julian_year_from_fixed(Options.day_end);
for (int y = year1; y <= year2; y++) {
if (year >= 0 && year != y)
continue;
date_set(&date, y, month, day);
rd = fixed_from_julian(&date);
if ((dp = find_rd(rd, 0)) != NULL) {
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats", __func__);
return count;
}
dayp[count++] = dp;
}
}
return count;
}
int
julian_find_days_dom(int dom, struct cal_day **dayp, char **edp __unused)
{
struct cal_day *dp;
struct date date;
int year1, year2;
int rd_begin, rd_end;
int count = 0;
year1 = julian_year_from_fixed(Options.day_begin);
year2 = julian_year_from_fixed(Options.day_end);
for (int y = year1; y <= year2; y++) {
date_set(&date, y, 1, 1);
rd_begin = fixed_from_julian(&date);
date.year++;
rd_end = fixed_from_julian(&date);
if (rd_end > Options.day_end)
rd_end = Options.day_end;
for (int m = 1, rd = rd_begin; rd <= rd_end; m++) {
date_set(&date, y, m, dom);
rd = fixed_from_julian(&date);
if ((dp = find_rd(rd, 0)) != NULL) {
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats",
__func__);
return count;
}
dayp[count++] = dp;
}
}
}
return count;
}
int
julian_find_days_month(int month, struct cal_day **dayp, char **edp __unused)
{
struct cal_day *dp;
struct date date;
int year1, year2;
int rd_begin, rd_end;
int count = 0;
year1 = julian_year_from_fixed(Options.day_begin);
year2 = julian_year_from_fixed(Options.day_end);
for (int y = year1; y <= year2; y++) {
date_set(&date, y, month, 1);
rd_begin = fixed_from_julian(&date);
date.month++;
if (date.month > 12)
date_set(&date, y+1, 1, 1);
rd_end = fixed_from_julian(&date);
if (rd_end > Options.day_end)
rd_end = Options.day_end;
for (int rd = rd_begin; rd <= rd_end; rd++) {
if ((dp = find_rd(rd, 0)) != NULL) {
if (count >= CAL_MAX_REPEAT) {
warnx("%s: too many repeats",
__func__);
return count;
}
dayp[count++] = dp;
}
}
}
return count;
}
void
show_julian_calendar(int rd)
{
struct date gdate, jdate;
bool leap;
gregorian_from_fixed(rd, &gdate);
julian_from_fixed(rd, &jdate);
leap = julian_leap_year(jdate.year);
printf("Gregorian date: %d-%02d-%02d\n",
gdate.year, gdate.month, gdate.day);
printf("Julian date: %d-%02d-%02d\n",
jdate.year, jdate.month, jdate.day);
printf("Leap year: %s\n", leap ? "yes" : "no");
}