initial proper of seasons
This commit is contained in:
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Compiled output
|
||||||
|
calendarium
|
||||||
|
*.o
|
||||||
|
*.out
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Editor files
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Rob Harbaugh
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
7
README.md
Normal file
7
README.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# calendarium
|
||||||
|
Calculate the church calendar for the current year.
|
||||||
|
|
||||||
|
Right now, when you run it, you get a printout of every day for your current calendar year.
|
||||||
|
This is opinionated - it's the current USCCB calendar at first.
|
||||||
|
|
||||||
|
Eventually there'll be args for specific days, including readings and stuff.
|
||||||
505
src/builder.c
Normal file
505
src/builder.c
Normal file
@@ -0,0 +1,505 @@
|
|||||||
|
#include "builder.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
//helper to add days to a date
|
||||||
|
static struct tm add_days(struct tm date, int days) {
|
||||||
|
date.tm_mday += days;
|
||||||
|
mktime(&date);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper to compare dates (return negative if d1 < d2, 0 if equal, positive if d1 > d2)
|
||||||
|
static int compare_dates(struct tm d1, struct tm d2) {
|
||||||
|
if (d1.tm_year != d2.tm_year) return d1.tm_year - d2.tm_year;
|
||||||
|
if (d1.tm_mon != d2.tm_mon) return d1.tm_mon - d2.tm_mon;
|
||||||
|
return d1.tm_mday - d2.tm_mday;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computus algorithm to calculate Easter Sunday
|
||||||
|
static ChurchDay easter_for_year(int year) {
|
||||||
|
int a = year % 19;
|
||||||
|
int b = year / 100;
|
||||||
|
int c = year % 100;
|
||||||
|
int d = b / 4;
|
||||||
|
int e = b % 4;
|
||||||
|
int f = (b + 8) / 25;
|
||||||
|
int g = (b - f + 1) / 3;
|
||||||
|
int h = (19 * a + b - d - g + 15) % 30;
|
||||||
|
int i = c / 4;
|
||||||
|
int k = c % 4;
|
||||||
|
int l = (32 + 2 * e + 2 * i - h - k) % 7;
|
||||||
|
int m = (a + 11 * h + 22 * l) / 451;
|
||||||
|
int month = (h + l - 7 * m + 114) / 31;
|
||||||
|
int day = (h + l - 7 * m + 114) % 31 + 1;
|
||||||
|
|
||||||
|
return church_day_new(year, month, day, SOLEMNITY, EASTER,
|
||||||
|
"Easter Sunday of the Resurrection of the Lord");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ChurchDay ash_wednesday(const ChurchDay *easter) {
|
||||||
|
struct tm date = add_days(easter->date, -46);
|
||||||
|
ChurchDay cd;
|
||||||
|
cd.date = date;
|
||||||
|
cd.class = SOLEMNITY;
|
||||||
|
cd.season = LENT;
|
||||||
|
strcpy(cd.description, "Ash Wednesday");
|
||||||
|
return cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ChurchDay first_sunday_advent(int year) {
|
||||||
|
struct tm dec_first = {0};
|
||||||
|
dec_first.tm_year = year - 1900;
|
||||||
|
dec_first.tm_mon = 11; // December
|
||||||
|
dec_first.tm_mday = 1;
|
||||||
|
mktime(&dec_first);
|
||||||
|
|
||||||
|
int days_to_subtract;
|
||||||
|
switch (dec_first.tm_wday) {
|
||||||
|
case 0: days_to_subtract = 0; break; // Sunday
|
||||||
|
case 1: days_to_subtract = 1; break; // Monday
|
||||||
|
case 2: days_to_subtract = 2; break; // Tuesday
|
||||||
|
case 3: days_to_subtract = 3; break; // Wednesday
|
||||||
|
case 4: days_to_subtract = 4; break; // Thursday
|
||||||
|
case 5: days_to_subtract = -2; break; // Friday
|
||||||
|
case 6: days_to_subtract = -1; break; // Saturday
|
||||||
|
default: days_to_subtract = 0; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct tm date = add_days(dec_first, -days_to_subtract);
|
||||||
|
ChurchDay cd;
|
||||||
|
cd.date = date;
|
||||||
|
cd.class = SUNDAY;
|
||||||
|
cd.season = ADVENT;
|
||||||
|
strcpy(cd.description, "1st Sunday of Advent");
|
||||||
|
return cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ChurchDay epiphany(int year) {
|
||||||
|
for (int day = 2; day <= 8; day++) {
|
||||||
|
struct tm date = {0};
|
||||||
|
date.tm_year = year - 1900;
|
||||||
|
date.tm_mon = 0; // January
|
||||||
|
date.tm_mday = day;
|
||||||
|
mktime(&date);
|
||||||
|
|
||||||
|
if (date.tm_wday == 0) { // Sunday
|
||||||
|
return church_day_new(year, 1, day, SOLEMNITY, CHRISTMAS,
|
||||||
|
"The Epiphany of the Lord");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback (shouldn't happen)
|
||||||
|
return church_day_new(year, 1, 6, SOLEMNITY, CHRISTMAS, "The Epiphany of the Lord");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ChurchDay baptism(ChurchDay epiphany_day) {
|
||||||
|
struct tm date = add_days(epiphany_day.date, 7);
|
||||||
|
ChurchDay cd;
|
||||||
|
cd.date = date;
|
||||||
|
cd.class = FEAST;
|
||||||
|
cd.season = CHRISTMAS;
|
||||||
|
strcpy(cd.description, "The Baptism of the Lord");
|
||||||
|
return cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ChurchDay pentecost(ChurchDay easter) {
|
||||||
|
struct tm date = add_days(easter.date, 49);
|
||||||
|
ChurchDay cd;
|
||||||
|
cd.date = date;
|
||||||
|
cd.class = SOLEMNITY;
|
||||||
|
cd.season = EASTER;
|
||||||
|
strcpy(cd.description, "Pentecost Sunday");
|
||||||
|
return cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* ordinal_suffix(int number) {
|
||||||
|
char str[16];
|
||||||
|
snprintf(str, sizeof(str), "%d", number);
|
||||||
|
|
||||||
|
if (strstr(str, "11") == str + strlen(str) - 2 ||
|
||||||
|
strstr(str, "12") == str + strlen(str) - 2 ||
|
||||||
|
strstr(str, "13") == str + strlen(str) - 2) {
|
||||||
|
return "th";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (str[strlen(str) - 1]) {
|
||||||
|
case '1': return "st";
|
||||||
|
case '2': return "nd";
|
||||||
|
case '3': return "rd";
|
||||||
|
default: return "th";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* day_string(int wday) {
|
||||||
|
switch (wday) {
|
||||||
|
case 0: return "Sunday";
|
||||||
|
case 1: return "Monday";
|
||||||
|
case 2: return "Tuesday";
|
||||||
|
case 3: return "Wednesday";
|
||||||
|
case 4: return "Thursday";
|
||||||
|
case 5: return "Friday";
|
||||||
|
case 6: return "Saturday";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calculate_advent(ChurchYear *cy, const ChurchDay *start) {
|
||||||
|
ChurchDay current = *start;
|
||||||
|
int sunday_counter = 1;
|
||||||
|
|
||||||
|
church_year_add(cy, current);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (current.date.tm_mon == 11 && current.date.tm_mday == 24) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.date = add_days(current.date, 1);
|
||||||
|
|
||||||
|
if (current.date.tm_wday == 0) {
|
||||||
|
sunday_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.season = ADVENT;
|
||||||
|
current.class = (current.date.tm_wday == 0) ? SUNDAY : SEASONAL_WEEKDAY;
|
||||||
|
|
||||||
|
if (current.date.tm_mday >= 17 && current.date.tm_mon == 11) {
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"December %d%s", current.date.tm_mday,
|
||||||
|
ordinal_suffix(current.date.tm_mday));
|
||||||
|
} else if (current.date.tm_wday == 0) {
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%d%s Sunday of Advent", sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
} else {
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%s of the %d%s week of Advent",
|
||||||
|
day_string(current.date.tm_wday), sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
church_year_add(cy, current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calculate_christmas(ChurchYear *cy, int year) {
|
||||||
|
ChurchDay epiphany_day = epiphany(year + 1);
|
||||||
|
ChurchDay baptism_day = baptism(epiphany_day);
|
||||||
|
|
||||||
|
ChurchDay christmas = church_day_new(year, 12, 25, SOLEMNITY, CHRISTMAS,
|
||||||
|
"The Nativity of the Lord (Christmas)");
|
||||||
|
church_year_add(cy, christmas);
|
||||||
|
|
||||||
|
ChurchDay current = christmas;
|
||||||
|
while (1) {
|
||||||
|
struct tm next_date = add_days(current.date, 1);
|
||||||
|
if (next_date.tm_mon == epiphany_day.date.tm_mon &&
|
||||||
|
next_date.tm_mday == epiphany_day.date.tm_mday) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.date = next_date;
|
||||||
|
current.season = CHRISTMAS;
|
||||||
|
current.class = (current.date.tm_wday == 0) ? FEAST : SEASONAL_WEEKDAY;
|
||||||
|
|
||||||
|
if (current.date.tm_wday == 0) {
|
||||||
|
strcpy(current.description, "The Holy Family of Jesus, Mary, and Joseph");
|
||||||
|
} else {
|
||||||
|
switch (current.date.tm_mday) {
|
||||||
|
case 26: strcpy(current.description, "Saint Stephen, First Martyr"); break;
|
||||||
|
case 27: strcpy(current.description, "Saint John, Apostle and Evangelist"); break;
|
||||||
|
case 28: strcpy(current.description, "Holy Innocents"); break;
|
||||||
|
case 29: strcpy(current.description, "Fifth Day within the Octave of the Nativity of the Lord"); break;
|
||||||
|
case 30: strcpy(current.description, "Sixth Day within the Octave of the Nativity of the Lord"); break;
|
||||||
|
case 31: strcpy(current.description, "Seventh Day within the Octave of the Nativity of the Lord"); break;
|
||||||
|
default: strcpy(current.description, "Christmas Weekday"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
church_year_add(cy, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
church_year_add(cy, epiphany_day);
|
||||||
|
|
||||||
|
current = epiphany_day;
|
||||||
|
while (1) {
|
||||||
|
struct tm next_date = add_days(current.date, 1);
|
||||||
|
if (next_date.tm_mon == baptism_day.date.tm_mon &&
|
||||||
|
next_date.tm_mday == baptism_day.date.tm_mday) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.date = next_date;
|
||||||
|
current.season = CHRISTMAS;
|
||||||
|
current.class = SEASONAL_WEEKDAY;
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%s between Epiphany and Baptism of the Lord",
|
||||||
|
day_string(current.date.tm_wday));
|
||||||
|
|
||||||
|
church_year_add(cy, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
church_year_add(cy, baptism_day);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calculate_lent(ChurchYear *cy, ChurchDay ash_wed) {
|
||||||
|
ChurchDay easter = easter_for_year(ash_wed.date.tm_year + 1900);
|
||||||
|
struct tm stop_date = add_days(easter.date, -8);
|
||||||
|
|
||||||
|
ChurchDay current = ash_wed;
|
||||||
|
int sunday_counter = 0;
|
||||||
|
|
||||||
|
church_year_add(cy, current);
|
||||||
|
|
||||||
|
while (compare_dates(current.date, stop_date) != 0) {
|
||||||
|
current.date = add_days(current.date, 1);
|
||||||
|
|
||||||
|
if (current.date.tm_wday == 0) {
|
||||||
|
sunday_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.season = LENT;
|
||||||
|
current.class = (current.date.tm_wday == 0) ? SUNDAY : SEASONAL_WEEKDAY;
|
||||||
|
|
||||||
|
if (sunday_counter == 0) {
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%s after Ash Wednesday", day_string(current.date.tm_wday));
|
||||||
|
} else if (current.date.tm_wday == 0) {
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%d%s Sunday of Lent", sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
} else {
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%s of the %d%s week of Lent",
|
||||||
|
day_string(current.date.tm_wday), sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
church_year_add(cy, current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calculate_easter(ChurchYear *cy, ChurchDay sat_before) {
|
||||||
|
ChurchDay easter = easter_for_year(sat_before.date.tm_year + 1900);
|
||||||
|
struct tm ps_date = add_days(easter.date, -7);
|
||||||
|
|
||||||
|
ChurchDay palm_sunday;
|
||||||
|
palm_sunday.date = ps_date;
|
||||||
|
palm_sunday.class = SUNDAY;
|
||||||
|
palm_sunday.season = LENT;
|
||||||
|
strcpy(palm_sunday.description, "Palm Sunday of the Passion of the Lord");
|
||||||
|
church_year_add(cy, palm_sunday);
|
||||||
|
|
||||||
|
const char* holy_week_names[] = {
|
||||||
|
"",
|
||||||
|
"Monday of Holy Week",
|
||||||
|
"Tuesday of Holy Week",
|
||||||
|
"Wednesday of Holy Week",
|
||||||
|
"Holy Thursday",
|
||||||
|
"Friday of the Passion of the Lord",
|
||||||
|
"Holy Saturday"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int days_after = 1; days_after <= 6; days_after++) {
|
||||||
|
ChurchDay day;
|
||||||
|
day.date = add_days(ps_date, days_after);
|
||||||
|
day.class = (days_after < 4) ? SEASONAL_WEEKDAY : SOLEMNITY;
|
||||||
|
day.season = (days_after < 4) ? LENT : TRIDUUM;
|
||||||
|
strcpy(day.description, holy_week_names[days_after]);
|
||||||
|
church_year_add(cy, day);
|
||||||
|
}
|
||||||
|
|
||||||
|
church_year_add(cy, easter);
|
||||||
|
|
||||||
|
const char* octave_names[] = {
|
||||||
|
"",
|
||||||
|
"Monday within the Octave of Easter",
|
||||||
|
"Tuesday within the Octave of Easter",
|
||||||
|
"Wednesday within the Octave of Easter",
|
||||||
|
"Thursday within the Octave of Easter",
|
||||||
|
"Friday within the Octave of Easter",
|
||||||
|
"Saturday within the Octave of Easter",
|
||||||
|
"Sunday of Divine Mercy"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int octave_days = 1; octave_days <= 7; octave_days++) {
|
||||||
|
ChurchDay day;
|
||||||
|
day.date = add_days(easter.date, octave_days);
|
||||||
|
day.class = SOLEMNITY;
|
||||||
|
day.season = EASTER;
|
||||||
|
strcpy(day.description, octave_names[octave_days]);
|
||||||
|
church_year_add(cy, day);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChurchDay pentecost_day = pentecost(easter);
|
||||||
|
struct tm stop_date = add_days(pentecost_day.date, -1);
|
||||||
|
|
||||||
|
ChurchDay current = cy->days[cy->count - 1];
|
||||||
|
int sunday_counter = 2;
|
||||||
|
|
||||||
|
while (compare_dates(current.date, stop_date) != 0) {
|
||||||
|
current.date = add_days(current.date, 1);
|
||||||
|
|
||||||
|
if (current.date.tm_wday == 0) {
|
||||||
|
sunday_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.season = EASTER;
|
||||||
|
current.class = (current.date.tm_wday == 0) ? SUNDAY : SEASONAL_WEEKDAY;
|
||||||
|
|
||||||
|
if (current.date.tm_wday == 0) {
|
||||||
|
if (sunday_counter == 7) {
|
||||||
|
strcpy(current.description, "The Ascension of the Lord");
|
||||||
|
} else {
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%d%s Sunday of Easter", sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%s of the %d%s week of Easter",
|
||||||
|
day_string(current.date.tm_wday), sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
church_year_add(cy, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
church_year_add(cy, pentecost_day);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calculate_otime_prelent(ChurchYear *cy, ChurchDay baptism_day) {
|
||||||
|
ChurchDay easter = easter_for_year(baptism_day.date.tm_year + 1900);
|
||||||
|
ChurchDay ash_wed = ash_wednesday(&easter);
|
||||||
|
struct tm stop_date = add_days(ash_wed.date, -1);
|
||||||
|
|
||||||
|
ChurchDay current;
|
||||||
|
current.date = add_days(baptism_day.date, 1);
|
||||||
|
current.class = FERIAL_WEEKDAY;
|
||||||
|
current.season = ORDINARY_TIME;
|
||||||
|
strcpy(current.description, "Monday of the 1st week in Ordinary Time");
|
||||||
|
|
||||||
|
int sunday_counter = 1;
|
||||||
|
church_year_add(cy, current);
|
||||||
|
|
||||||
|
while (compare_dates(current.date, stop_date) != 0) {
|
||||||
|
current.date = add_days(current.date, 1);
|
||||||
|
|
||||||
|
if (current.date.tm_wday == 0) {
|
||||||
|
sunday_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.season = ORDINARY_TIME;
|
||||||
|
current.class = (current.date.tm_wday == 0) ? SUNDAY : FERIAL_WEEKDAY;
|
||||||
|
|
||||||
|
if (current.date.tm_wday == 0) {
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%d%s Sunday of Ordinary Time", sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
} else {
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%s of the %d%s week of Ordinary Time",
|
||||||
|
day_string(current.date.tm_wday), sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
church_year_add(cy, current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void calculate_otime_posteaster(ChurchYear *cy, ChurchDay pentecost_day) {
|
||||||
|
ChurchDay first_advent = first_sunday_advent(pentecost_day.date.tm_year + 1900);
|
||||||
|
struct tm last_day = add_days(first_advent.date, -1);
|
||||||
|
|
||||||
|
struct tm trinity_sunday = add_days(pentecost_day.date, 7);
|
||||||
|
struct tm corpus_christi = add_days(trinity_sunday, 7);
|
||||||
|
|
||||||
|
// Build backwards
|
||||||
|
ChurchDay *temp_days = malloc(366 * sizeof(ChurchDay));
|
||||||
|
size_t temp_count = 0;
|
||||||
|
|
||||||
|
ChurchDay current;
|
||||||
|
current.date = last_day;
|
||||||
|
current.class = FERIAL_WEEKDAY;
|
||||||
|
current.season = ORDINARY_TIME;
|
||||||
|
int sunday_counter = 34;
|
||||||
|
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%s of the %d%s Week in Ordinary Time",
|
||||||
|
day_string(current.date.tm_wday), sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
temp_days[temp_count++] = current;
|
||||||
|
|
||||||
|
struct tm stop_date = add_days(pentecost_day.date, 1);
|
||||||
|
|
||||||
|
while (compare_dates(current.date, stop_date) != 0) {
|
||||||
|
current.date = add_days(current.date, -1);
|
||||||
|
|
||||||
|
if (current.date.tm_wday == 0) {
|
||||||
|
sunday_counter--;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.season = ORDINARY_TIME;
|
||||||
|
|
||||||
|
if (current.date.tm_wday == 0) {
|
||||||
|
if (compare_dates(current.date, trinity_sunday) == 0) {
|
||||||
|
current.class = SOLEMNITY;
|
||||||
|
strcpy(current.description, "The Most Holy Trinity");
|
||||||
|
} else if (compare_dates(current.date, corpus_christi) == 0) {
|
||||||
|
current.class = SOLEMNITY;
|
||||||
|
strcpy(current.description, "The Most Holy Body and Blood of Christ");
|
||||||
|
} else if (sunday_counter == 33) {
|
||||||
|
current.class = SOLEMNITY;
|
||||||
|
strcpy(current.description, "Our Lord Jesus Christ, King of the Universe");
|
||||||
|
} else {
|
||||||
|
current.class = SUNDAY;
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%d%s Sunday of Ordinary Time", sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current.class = FERIAL_WEEKDAY;
|
||||||
|
snprintf(current.description, sizeof(current.description),
|
||||||
|
"%s of the %d%s week of Ordinary Time",
|
||||||
|
day_string(current.date.tm_wday), sunday_counter,
|
||||||
|
ordinal_suffix(sunday_counter));
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_days[temp_count++] = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse and add to main list
|
||||||
|
for (size_t i = temp_count; i > 0; i--) {
|
||||||
|
church_year_add(cy, temp_days[i - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(temp_days);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChurchYear proper_of_seasons(struct tm today) {
|
||||||
|
ChurchYear cy = church_year_new();
|
||||||
|
|
||||||
|
ChurchDay first_advent = first_sunday_advent(today.tm_year + 1900);
|
||||||
|
if (compare_dates(today, first_advent.date) < 0) {
|
||||||
|
first_advent = first_sunday_advent(today.tm_year + 1900 - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculate_advent(&cy, &first_advent);
|
||||||
|
calculate_christmas(&cy, first_advent.date.tm_year + 1900);
|
||||||
|
|
||||||
|
ChurchDay baptism_day = cy.days[cy.count - 1];
|
||||||
|
ChurchDay easter = easter_for_year(baptism_day.date.tm_year + 1900);
|
||||||
|
ChurchDay ash_wed = ash_wednesday(&easter);
|
||||||
|
|
||||||
|
calculate_otime_prelent(&cy, baptism_day);
|
||||||
|
calculate_lent(&cy, ash_wed);
|
||||||
|
|
||||||
|
ChurchDay sat_before = cy.days[cy.count - 1];
|
||||||
|
calculate_easter(&cy, sat_before);
|
||||||
|
|
||||||
|
ChurchDay pentecost_day = cy.days[cy.count - 1];
|
||||||
|
calculate_otime_posteaster(&cy, pentecost_day);
|
||||||
|
|
||||||
|
return cy;
|
||||||
|
}
|
||||||
10
src/builder.h
Normal file
10
src/builder.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef BUILDER_H
|
||||||
|
#define BUILDER_H
|
||||||
|
|
||||||
|
#include "church_day.h"
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
//calculate proper of seasons for a given date
|
||||||
|
ChurchYear proper_of_seasons(struct tm today);
|
||||||
|
|
||||||
|
#endif //BUILDER_H
|
||||||
81
src/church_day.c
Normal file
81
src/church_day.c
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include "church_day.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
ChurchDay church_day_new(int year, int month, int day, DayClass class, Season season, const char *description) {
|
||||||
|
ChurchDay cd;
|
||||||
|
memset(&cd.date, 0, sizeof(struct tm));
|
||||||
|
|
||||||
|
cd.date.tm_year = year - 1900;
|
||||||
|
cd.date.tm_mon = month - 1;
|
||||||
|
cd.date.tm_mday = day;
|
||||||
|
|
||||||
|
mktime(&cd.date);
|
||||||
|
|
||||||
|
cd.class = class;
|
||||||
|
cd.season = season;
|
||||||
|
strncpy(cd.description, description, sizeof(cd.description) - 1);
|
||||||
|
cd.description[sizeof(cd.description) - 1] = '\0';
|
||||||
|
|
||||||
|
return cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* church_day_month_name(const ChurchDay *day) {
|
||||||
|
static char buffer[32];
|
||||||
|
strftime(buffer, sizeof(buffer), "%B", &day->date);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* church_day_weekday_string(const ChurchDay *day) {
|
||||||
|
static char buffer[32];
|
||||||
|
strftime(buffer, sizeof(buffer), "%A", &day->date);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* day_class_to_string(DayClass class) {
|
||||||
|
switch (class) {
|
||||||
|
case SOLEMNITY: return "Solemnity";
|
||||||
|
case SUNDAY: return "Sunday";
|
||||||
|
case FEAST: return "Feast";
|
||||||
|
case MEMORIAL: return "Memorial";
|
||||||
|
case SEASONAL_WEEKDAY: return "Seasonal Weekday";
|
||||||
|
case FERIAL_WEEKDAY: return "Ferial";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* season_to_string(Season season) {
|
||||||
|
switch(season) {
|
||||||
|
case ADVENT: return "Advent";
|
||||||
|
case CHRISTMAS: return "Christmas";
|
||||||
|
case ORDINARY_TIME: return "Ordinary Time";
|
||||||
|
case LENT: return "Lent";
|
||||||
|
case TRIDUUM: return "Sacred Triduum";
|
||||||
|
case EASTER: return "Easter";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChurchYear church_year_new(void) {
|
||||||
|
ChurchYear cy;
|
||||||
|
cy.capacity = 366;
|
||||||
|
cy.count = 0;
|
||||||
|
cy.days = malloc(cy.capacity * sizeof(ChurchDay));
|
||||||
|
return cy;
|
||||||
|
}
|
||||||
|
|
||||||
|
void church_year_add(ChurchYear *year, ChurchDay day) {
|
||||||
|
if (year->count >= year->capacity) {
|
||||||
|
year->capacity *= 2;
|
||||||
|
year->days = realloc(year->days, year->capacity * sizeof(ChurchDay));
|
||||||
|
}
|
||||||
|
year->days[year->count++] = day;
|
||||||
|
}
|
||||||
|
|
||||||
|
void church_year_free(ChurchYear *year) {
|
||||||
|
free(year->days);
|
||||||
|
year->days = NULL;
|
||||||
|
year->count = 0;
|
||||||
|
year->capacity = 0;
|
||||||
|
}
|
||||||
57
src/church_day.h
Normal file
57
src/church_day.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#ifndef CHURCH_DAY_H
|
||||||
|
#define CHURCH_DAY_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SOLEMNITY,
|
||||||
|
SUNDAY,
|
||||||
|
FEAST,
|
||||||
|
MEMORIAL,
|
||||||
|
SEASONAL_WEEKDAY,
|
||||||
|
FERIAL_WEEKDAY
|
||||||
|
} DayClass;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ADVENT,
|
||||||
|
CHRISTMAS,
|
||||||
|
ORDINARY_TIME,
|
||||||
|
LENT,
|
||||||
|
TRIDUUM,
|
||||||
|
EASTER
|
||||||
|
} Season;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
struct tm date;
|
||||||
|
DayClass class;
|
||||||
|
Season season;
|
||||||
|
char description[256];
|
||||||
|
} ChurchDay;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ChurchDay *days;
|
||||||
|
size_t count;
|
||||||
|
size_t capacity;
|
||||||
|
} ChurchYear;
|
||||||
|
|
||||||
|
//Create a new ChurchDay
|
||||||
|
ChurchDay church_day_new(int year, int month, int day, DayClass class, Season season, const char *description);
|
||||||
|
|
||||||
|
//Get month name from ChurchDay
|
||||||
|
const char* church_day_month_name(const ChurchDay *day);
|
||||||
|
|
||||||
|
//Get day of week string from ChurchDay
|
||||||
|
const char* church_day_weekday_string(const ChurchDay *day);
|
||||||
|
|
||||||
|
//Get string representation of DayClass
|
||||||
|
const char* day_class_to_string(DayClass class);
|
||||||
|
|
||||||
|
//Get string representation of Season
|
||||||
|
const char* season_to_string(Season season);
|
||||||
|
|
||||||
|
//ChurchYear functions
|
||||||
|
ChurchYear church_year_new(void);
|
||||||
|
void church_year_add(ChurchYear *year, ChurchDay day);
|
||||||
|
void church_year_free(ChurchYear *year);
|
||||||
|
|
||||||
|
#endif // CHURCH_DAY_H
|
||||||
26
src/main.c
Normal file
26
src/main.c
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "church_day.h"
|
||||||
|
#include "builder.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
time_t now = time(NULL);
|
||||||
|
struct tm *today = localtime(&now);
|
||||||
|
|
||||||
|
ChurchYear seasons = proper_of_seasons(*today);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < seasons.count; i++) {
|
||||||
|
ChurchDay *day = &seasons.days[i];
|
||||||
|
printf("%s, %s %d, %d - %s - %s in %s\n",
|
||||||
|
church_day_weekday_string(day),
|
||||||
|
church_day_month_name(day),
|
||||||
|
day->date.tm_mday,
|
||||||
|
day->date.tm_year + 1900,
|
||||||
|
day->description,
|
||||||
|
day_class_to_string(day->class),
|
||||||
|
season_to_string(day->season));
|
||||||
|
}
|
||||||
|
|
||||||
|
church_year_free(&seasons);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user