#ident "$Id: luach-ical.c,v 1.29 2007/03/11 14:31:07 rl Exp $" /* * Hebrew - Gregorian Calendar (iCalendar) * * Author: * Dr. Zvi Har'El, ד"ר צבי הראל * Department of Mathematics, הפקולטה למתמטיקה * Technion, Israel Institute of Technology, הטכניון - מט"ל * Haifa 32000, Israel. חיפה 32000, ישראל * E-Mail: rl@math.technion.ac.il * * See: * Dawson, F. and D. Stenerson, Internet Calendaring and Scheduling Core * Object Specification (iCalendar), RFC2445, November 1998. */ #include #include #include #include extern int Gauss(int year, int g, int* day); extern void alef(int n, int i, char* s); static char* holidays(int month, int length, int wd, bool dflag, bool hflag, int year); static char* reading(int month, int length, bool leap, bool dflag, bool hflag); static char* omer(int day, int month, bool hflag); static void vcalendar_begin(char *desc, bool g); static void vcalendar_end(); static void vevent(char* desc, int year, int month, int day); static int Hlen[13] = { 30, 0, 0, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29 }; static int Glen[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; static char *Hmonth[13][2] = { { "Tishri", "תשרי" }, { "Heshvan", "חשון" }, { "Kislev", "כסלו" }, { "Tevet", "טבת" }, { "Shevat", "שבט" }, { "Adar I", "אדר א" }, { "", "" }, { "Nisan", "ניסן" }, { "Iyyar", "איר" }, { "Sivan", "סיון" }, { "Tammuz", "תמוז" }, { "Av", "אב" }, { "Elul", "אלול"} }; static const char *portion[54][2] = { { "Bereshit", "בראשית" }, { "Noach", "נח" }, { "Lech-Lecha", "לך־לך" }, { "Vayera", "וירא" }, { "Chayei-Sarah", "חיי־שרה" }, { "Toldot", "תולדת" }, { "Vayetze", "ויצא" }, { "Vayishlach", "וישלח" }, { "Vayeshev", "וישב" }, { "Miketz", "מקץ" }, { "Vayigash", "ויגש" }, { "Vayechi", "ויחי" }, { "Shemot", "שמות" }, { "Vaera", "וארא" }, { "Bo", "בא" }, { "Beshalach", "בשלח" }, { "Yitro", "יתרו" }, { "Mishpatim", "משפטים" }, { "Teruma", "תרומה" }, { "Tetzave", "תצוה" }, { "Ki-Tisa", "כי־תשא" }, { "Vayakhel", "ויקהל" }, { "Pekudei", "פקודי" }, { "Vayikra", "ויקרא" }, { "Tzav", "צו" }, { "Shemini", "שמיני" }, { "Tazri’a", "תזריע" }, { "Metzora", "מצרע" }, { "Acharei-Mot", "אחרי־מות" }, { "Kedoshim", "קדשים" }, { "Emor", "אמר" }, { "Behar", "בהר" }, { "Bechukotai", "בחקתי" }, { "Bamidbar", "במדבר" }, { "Naso", "נשא" }, { "Beha’alotcha", "בהעלתך" }, { "Shelach", "שלח" }, { "Korach", "קרח" }, { "Chukat", "חקת" }, { "Balak", "בלק" }, { "Pinchas", "פינחס" }, { "Matot", "מטות" }, { "Mase’ei", "מסעי" }, { "Devarim", "דברים" }, { "Vaetchanan", "ואתחנן" }, { "Ekev", "עקב" }, { "Re’e", "ראה" }, { "Shoftim", "שפטים" }, { "Ki-Tetze", "כי־תצא" }, { "Ki-Tavo", "כי־תבוא" }, { "Nitzavim", "נצבים" }, { "Vayelech", "וילך" }, { "Ha’azinu", "האזינו" }, { "Vezot-Habracha", "וזאת־הברכה" } }; int main(int argc, const char **argv) { bool dflag = false; //Israel/Diaspora style bool hflag = false; //English/Hebrew printout bool jflag = false; //Gregorian/Julian calendar bool xflag = false; //Exclude date and readings while (--argc > 0) { const char *p; int Hy; //Hebrew year (A.M.) if (*(p = *++argv) == '-') { while (*++p) switch(*p) { case 'e': //English printout hflag = false; break; case 'h': //Hebrew printout hflag = true; break; case 'i': //reading Israel style dflag = false; break; case 'd': //reading Diaspora style, second holidays dflag = true; break; case 'g': //Gregorian calendar jflag = false; break; case 'j': //Julian Calendar jflag = true; break; case 'x': //Exclude dates and readings xflag = true; break; case 'a': //All items xflag = false; break; default: fprintf(stderr,"illegal option: %s\n", *argv); exit(1); } } else if ((Hy = atoi(*argv))) { bool g = !jflag && Hy>= 5343; //Gregorian flag int Wd; //day in the week (mod. 7) int Gd = Gauss(Hy - 1, g, &Wd); //day in the Gregorian month (1:Glen[Gm]) int kvia = Gauss(Hy, g, NULL) - Gd + 365 - 353; //excess of length of the Hebrew year over 353 int Gy = Hy - 3760; //Gregorian year Glen[1] = 28; if ((Gy % 4 == 0 && (!g || Gy%100 != 0)) || Gy % 400 == 0) { kvia++; Glen[1]++; } Gy--; if (hflag) { Hmonth[6][1] = (kvia >= 30) ? "אדר ב" : "אדר"; } else { Hmonth[6][0] = (kvia >= 30) ? "Adar II" : "Adar"; } Hlen[1] = (kvia % 30 == 2) ? 30 : 29; Hlen[2] = (kvia % 30 == 0) ? 29 : 30; Wd+=2; if (Wd >= 7) Wd -= 7; int Gm; //month in the Gregorian year (0:11) if (Gd <= 21) { Gd += 10; Gm = 7; } else if (Gd <= 51) { Gd -= 21; Gm = 8; } else { Gd -= 51; Gm = 9; } int Hm = 0; //month in the Hebrew year (0:12) int Hd = 1; //day in the Hebrew month (1:Hlen[Hm]) if (g && Hy == 5343) Gd -= 10; char desc[100] = ""; if (hflag) { strcat(desc, "לוח עברי לשנת "); alef(Hy, 0, desc); strcat(desc, " נוסח "); strcat(desc, dflag? "הגולה" : "ישראל"); } else { sprintf(desc, "Hebrew Calendar for %d ", Hy); strcat(desc, dflag? "Diaspora" : "Israel"); strcat(desc, " Style"); } vcalendar_begin(desc, g); for (int i = 0; i < kvia + 353; i++) { desc[0] = '\0'; if (hflag) { alef(Hd, 0, desc); strcat(desc, " "); strcat(desc, Hmonth[Hm][1]); strcat(desc, " "); alef(Hy, 0, desc); } else { sprintf(desc, "%d %s %d", Hd, Hmonth[Hm][0], Hy); } if (!xflag) vevent(desc, Gy, Gm + 1, Gd); char *s = holidays(Hd, Hm, Wd, dflag, hflag, Hy); vevent(s, Gy, Gm + 1, Gd); if (Wd == 0 && !xflag) { s = reading(Hd, Hm, kvia >= 30, dflag, hflag); vevent(s, Gy, Gm + 1, Gd); } if (!xflag) { s = omer(Hd, Hm, hflag); vevent(s, Gy, Gm + 1, Gd); } if (Wd++ == 6) Wd = 0; if (Hd++ == Hlen[Hm]) { Hd = 1; Hm++; if (kvia < 30 && Hm == 5) Hm++; } if (Gd++ == Glen[Gm]) { Gd = 1; if (Gm++ == 11) { Gm = 0; Gy++; } } if (g && Hy == 5343 && Hm == 0 && Hd == 19) Gd += 10; } vcalendar_end(); } else { fprintf(stderr,"illegal argument: %s\n",*argv); exit(1); } } exit(0); } static char * holidays(int day, //Day the hebrew month (1:Hlen[month]) int month, //Hebrew Month (0:12) int wd, //Week day (0:6) bool dflag, //Diaspora second holidays bool hflag, //Hebrew printout int year) { static char *holiday[30]; if (day == 1) { for (int i = 0; i < 30; i++) holiday[i] = NULL; int head = wd; /* Week day of the first day of the month (0:6) */ holiday[0] = holiday[29] = !hflag ? "Rosh Chodesh" : "ראש חדש"; switch (month) { int i; case 0: holiday[0] = holiday[1] = !hflag ? "Rosh Hashana" : "ראש השנה"; holiday[2+(head==5)] = !hflag ? "Tzom Gdalia" : "צום גדליה"; holiday[9] = !hflag ? "Yom Kipur" : "יום כפור"; for (i = 14; i < 15 + dflag; i++) { holiday[i] = !hflag ? "Succot" : "סכות"; } for ( ; i < 20; i++) { holiday[i] = !hflag ? "Chol Hamoed Succot" : "חול המועד סכות"; } holiday[20] = !hflag ? "Hosha’ana Raba" : "הושענא רבה"; if (!dflag) { holiday[21] = !hflag ? "Shmini Atzeret/Simchat Tora" : "שמיני עצרת/שמחת תורה"; } else { holiday[21] = !hflag ? "Shmini Atzeret" : "שמיני עצרת"; holiday[22] = !hflag ? "Simchat Tora" : "שמחת תורה"; } break; case 2: for (i = 24; i < 29; i++) { holiday[i] = !hflag ? "Hannuka" : "חנכה"; } holiday[29] = !hflag ? "Hannuka/Rosh Chodesh" : "חנכה/ראש חדש"; break; case 3: holiday[0] = !hflag ? "Hannuka/Rosh Chodesh" : "חנכה/ראש חדש"; for (i = 1; i < 2 + (Hlen[2] == 29); i++) { holiday[i] = !hflag ? "Hannuka" : "חנכה"; } holiday[9+(head==5)] = !hflag ? "Tzom Asara B’Tevet" : "צום עשרה בטבת"; break; case 4: holiday[14] = !hflag ? "Tu BiShevat" : "ט״ו בשבט"; break; case 6: holiday[12-2*(head==2)] = !hflag ? "Taanit Ester" : "תענית אסתר"; holiday[13] = !hflag ? "Purim" : "פורים"; holiday[14] = !hflag ? "Shushan Purim" : "שושן פורים"; break; case 7: for (i = 14; i < 15 + dflag; i++) { holiday[i] = !hflag ? "Pesach" : "פסח"; } for ( ; i < 20; i++) { holiday[i] = !hflag ? "Chol Hamoed Pesach" : "חול המועד פסח"; } for ( ; i < 21 + dflag; i++) { holiday[i] = !hflag ? "Pesach" : "פסח"; } holiday[26+(head==3)] = !hflag ? "Yom Hashoa" : "יום השואה"; break; case 8: holiday[i=head==3?1:head==2?2:head==5&&year>=5764?4:3] = !hflag ? "Yom Hazikaron" : "יום הזכרון"; holiday[i+1] = !hflag ? "Yom Haatzmaut" : "יום העצמאות"; holiday[13] = !hflag ? "Pesach Sheni" : "פסח שני"; holiday[17] = !hflag ? "Lag BaOmer" : "ל״ג בעמר"; holiday[27] = !hflag ? "Yom Yerushalaim" : "יום ירושלים"; break; case 9: for (i = 5; i < 6 + dflag; i++) { holiday[i] = !hflag ? "Shavuot" : "שבועות"; } break; case 10: holiday[16+(head==5)] = !hflag ? "Tzom Shiv’a Asar B’Tammuz" : "צום שבעה עשר בתמוז"; break; case 11: holiday[8+(head==6)] = !hflag ? "Tzom Tish’a B’Av" : "צום תשעה באב"; holiday[14] = !hflag ? "Tu B’Av" : "ט״ו באב"; break; } } return holiday[day - 1]; } static char* reading(int day, //Day the hebrew month (1:Hlen[month]) int month, //Hebrew Month (0:12) bool leap, //leap year bool dflag, //Israel/Diaspora style bool hflag) //English/Hebrew printout { static bool holiday[5]; static char *special[5]; static int seq; //Portion sequence number (1:53) static int head; //Week day of the first day of the month (0:6) static char result[40]; if (day <= 7) { //Mark Saturdays which fall in a holiday in Tishri, Nisan and Sivan. //Mark special Saturday in Tishri. //Mark 4 special Saturdays in Adar or its vicinity. //Initialize reading sequence in Tishri. head = (8 - day) % 7; if (month == 0) { holiday[2] = true; if (head == 0) { seq = 53;//Haazinu holiday[0] = true; special[1] = !hflag ? "Shuva" : "שובה"; holiday[3] = true; } else if (head == 5) { seq = 53;//Haazinu special[0] = !hflag ? "Shuva" : "שובה"; holiday[1] = true; } else { seq = 52;//Vayelech special[0] = !hflag ? "Shuva" : "שובה"; } } else if (month == 5 || (month == 4 && !leap)) { if (head != 5) special[3 + (head == 0)] = !hflag ? "Shekalim" : "שקלים"; } else if (month == 6) { if (head == 0) special[0] = !hflag ? "Shekalim" : "שקלים"; special[1] = !hflag ? "Zachor" : "זכור"; special[2 + (head == 0 || head == 6)] = !hflag ? "Para" : "פרה"; if (head != 6) special[3 + (head == 0)] = !hflag ? "Hachodesh" : "החודש"; } else if (month == 7) { if (head == 0) special[0] = !hflag ? "Hachodesh" : "החודש"; holiday[2] = true; if (dflag && head == 0) holiday[3] = true; } else if (month == 9 && dflag && head == 1) { holiday[0] = true; } } int i = day / 7; //The ordinal number of this Saturday in the month if (holiday[i]) { holiday[i] = false; return NULL; } // Print reading sequence. // Check for possible joined reading. strcpy(result, portion[seq-1][hflag]); if ((seq == 22 /*Vayakhel/Pekudei*/ && !leap && (head != 0 || i == 4)) || (seq == 27 /*Tazria/Metzora*/ && !leap) || (seq == 29 /*Acharei/Kedoshim*/ && !leap) || (seq == 32 /*Behar/Bechukotai*/ && !leap && (dflag || head != 2)) || (seq == 39 /*Chukat/Balak*/ && dflag && head == 3) || (seq == 42 /*Matot/Masei*/ && (!leap || (dflag && month == 11) || (month == 10 && (head == 3 || (head == 1 && i == 3))))) || (seq == 51 /*Nitzavim/Vayelech*/ && (head == 4 || head == 6))) { strcat(result, "/"); strcat(result, portion[seq++][hflag]); } if (special[i]) { strcat(result, "/"); strcat(result, special[i]); special[i] = NULL; } if (seq++ == 53) seq = 1; return result; } static char* omer(int day, //Day the hebrew month (1:Hlen[month]) int month, //Hebrew Month (0:12) bool hflag) //English/Hebrew printout { static char result[10]; int count; if (month == 7 && day >= 16) count = day - 15; else if (month == 8) count = day + 15; else if (month == 9 && day <= 5) count = day + 44; else return NULL; if (hflag) { result[0] = '\0'; alef(count, 0, result); strcat(result, " בעמר"); } else { sprintf(result, "%d BaOmer", count); } return result; } static void vevent(char* desc, int year, int month, int day) { if (!desc) return; printf("BEGIN:VEVENT\r\n"); printf("SUMMARY:%s\r\n", desc); printf("DTSTART;VALUE=DATE:%.4d%.2d%.2d\r\n", year, month, day); printf("END:VEVENT\r\n"); } static void vcalendar_begin(char* desc, bool g) { printf("BEGIN:VCALENDAR\r\n"); printf("VERSION:2.0\r\n"); printf("PRODID:-//Gilead.org.il//NONSGML Luach-iCal $Revision: 1.29 $//EN\r\n"); printf("X-WR-CALNAME: %s\r\n", desc); if (g) printf("CALSCALE:GREGORIAN\r\n"); else printf("CALSCALE:JULIAN\r\n"); } static void vcalendar_end() { printf("END:VCALENDAR\r\n"); }