oswald/ui/calendar.c
2021-02-14 18:03:13 +01:00

209 lines
5.6 KiB
C

#include "oswald.h"
#include "calendar.h"
unsigned char is_leap(const unsigned int year)
{
/* the rule is, everything that can be devided by 4 is leap.
* Exception: the year can be devided by 100, then it is not,
* except it canbe devided by 400, then it is again.
*/
if ((year % 400) == 0)
return 1;
else if ((year % 100) == 0)
return 0;
else if ((year % 4) == 0)
return 1;
return 0;
}
unsigned short days_of_month(const unsigned int uMonat, const unsigned int uJahr)
{
// invalid,January,Febuary,March,April,May,June,July,August,September,October,November,December
int arrTageImMonat[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (uMonat == 2) {
// Febuary: distinguish leap
if (is_leap(uJahr))
return 29;
else
return 28;
}
if ((uMonat >= 1) && (uMonat <= 12))
return arrTageImMonat[uMonat];
else {
return 0;
}
}
short getAnzahlTageImJahr(const unsigned int uJahr)
{
return (is_leap(uJahr)) ? 366 : 365;
}
short getWochentag(const unsigned int uTag, const unsigned int uMonat, const unsigned int uJahr)
{
// ungült Jan Feb Mrz Apr Mai Jun Jul Aug Sep Okt Nov Dez
unsigned char arrMonatsOffset[13] = { 0, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
short nErgebnis = 0;
// Monat / Tag - Plausi prüfen:
if ((uTag > 31) || (uMonat > 12) || (uMonat <= 0)
|| (uTag <= 0) || (uJahr <= 0)) {
return -1;
}
unsigned char cbTagesziffer = (uTag % 7);
unsigned char cbMonatsziffer = arrMonatsOffset[uMonat];
unsigned char cbJahresziffer = ((uJahr % 100) + ((uJahr % 100) / 4)) % 7;
unsigned char cbJahrhundertziffer = (3 - ((uJahr / 100) % 4)) * 2;
// Schaltjahreskorrektur:
if ((uMonat <= 2) && (is_leap(uJahr)))
cbTagesziffer = cbTagesziffer + 6;
nErgebnis = (cbTagesziffer + cbMonatsziffer + cbJahresziffer + cbJahrhundertziffer) % 7;
// Ergebnis:
// 0 = Sonntag
// 1 = Montag
// 2 = Dienstag
// 3 = Mittwoch
// 4 = Donnerstag
// 5 = Freitag
// 6 = Samstag
return nErgebnis;
}
short getTagDesJahres(const unsigned int uTag, const unsigned int uMonat, const unsigned int uJahr)
{
// Der wievielte Tag des Jahres ist dieser Tag
if ((uMonat == 0) || (uMonat > 12)) {
return -1;
}
unsigned int uLokalTag = uTag;
unsigned int uLokalMonat = uMonat;
while (uLokalMonat > 1) {
uLokalMonat--;
uLokalTag += days_of_month(uLokalMonat, uJahr);
}
return uLokalTag;
}
short getKalenderwoche(short uTag, short uMonat, short uJahr)
{
// Berechnung erfolgt analog DIN 1355, welche besagt:
// Der erste Donnerstag im neuen Jahr liegt immer in der KW 1.
// "Woche" ist dabei definiert als [Mo, ..., So].
short nTagDesJahres = getTagDesJahres(uTag, uMonat, uJahr);
// Berechnen des Wochentags des 1. Januar:
short nWochentag1Jan = getWochentag(1, 1, uJahr);
// Sonderfälle Freitag und Samstag
if (nWochentag1Jan >= 5)
nWochentag1Jan = nWochentag1Jan - 7;
// Sonderfälle "Jahresanfang mit KW - Nummer aus dem Vorjahr"
if ( (nTagDesJahres + nWochentag1Jan) <= 1) {
return getKalenderwoche(31, 12, uJahr - 1);
}
short nKalenderWoche = ((nTagDesJahres + nWochentag1Jan + 5) / 7);
// 53 Kalenderwochen hat grundsätzlich nur ein Jahr,
// welches mit einem Donnerstag anfängt !
// In Schaltjahren ist es auch mit einem Mittwoch möglich, z.B. 1992
// Andernfalls ist diese KW schon die KW1 des Folgejahres.
if (nKalenderWoche == 53) {
boolean bIstSchaltjahr = is_leap(uJahr);
if ((nWochentag1Jan == 4) // Donnerstag
|| (nWochentag1Jan == -3) // auch Donnerstag
|| ((nWochentag1Jan == 3) && bIstSchaltjahr)
|| ((nWochentag1Jan == -4) && bIstSchaltjahr)) {
; // Das ist korrekt und erlaubt
} else
nKalenderWoche = 1; // Korrektur des Wertes
}
return nKalenderWoche;
}
void getOsterdatum(const unsigned int uJahr, unsigned int *uTag, unsigned int *uMonat)
{
// Berechnet für ein beliebiges Jahr das Osterdatum.
// Quelle des Gauss - Algorithmus: Stefan Gerth,
// "Die Gauß'sche Osterregel", Nürnberg, Februar 2003.
// http://krapfen.org/content/paper/Schule/Facharbeit/Berechnung_des_Osterfestes.pdf
unsigned int a = uJahr % 19;
unsigned int b = uJahr % 4;
unsigned int c = uJahr % 7;
int k = uJahr / 100;
int q = k / 4;
int p = ((8 * k) + 13) / 25;
unsigned int Egz = (38 - (k - q) + p) % 30; // Die Jahrhundertepakte
unsigned int M = (53 - Egz) % 30;
unsigned int N = (4 + k - q) % 7;
unsigned int d = ((19 * a) + M) % 30;
unsigned int e = ((2 * b) + (4 * c) + (6 * d) + N) % 7;
// Ausrechnen des Ostertermins:
if ((22 + d + e) <= 31) {
*uTag = 22 + d + e;
*uMonat = 3;
} else {
*uTag = d + e - 9;
*uMonat = 4;
// Zwei Ausnahmen berücksichtigen:
if (*uTag == 26)
*uTag = 19;
else if ((*uTag == 25) && (d == 28) && (a > 10))
*uTag = 18;
}
// Offsets für andere Feiertage:
// Schwerdonnerstag / Weiberfastnacht -52
// Rosenmontag -48
// Fastnachtsdienstag -47
// Aschermittwoch -46
// Gründonnerstag -3
// Karfreitag -2
// Ostersonntag 0
// Ostermontag +1
// Christi Himmelfahrt +39
// Pfingstsonntag +49
// Pfingstmontag +50
// Fronleichnam +60
// Mariä Himmelfahrt ist stets am 15. August (Danke an Michael Plugge!)
}
void getViertenAdvent(const unsigned int uJahr, unsigned int *uTag, unsigned int *uMonat)
{
// Berechnet für ein beliebiges Jahr das Datum des 4. Advents-Sonntags.
// Der 4. Adventssonntag ist stets der Sonntag vor dem 1. Weihnachtsfeiertag,
// muß also stets in der Periode [18. - 24.12.] liegen:
*uMonat = 12; // Das steht jedes Jahr fest :-)
short nWoTag = getWochentag(24, 12, uJahr); // Wochentag des 24.12. ermitteln
*uTag = 24 - nWoTag;
// Offsets: Der Buß- und Bettag liegt stets 32 Tage vor dem 4. Advent
}