amos@instable.UUCP (04/12/87)
Due to the growing number of requests, and the sorry state of some mailers (some of my replies have bounced 4 times already!) I've decided to post the new version to the net. It's more streamlined, and the machine-dependent parts are concentrated for easier porting, though I cannot guarantee it'll run everywhere. Good luck! Following is a shar file of the new release. Cut below and run through sh; watch out for the signature at the end. Khag Sameach! o / o / o / o / -----------------X---------------X---------------X---------------X---- o \ o \ o \ o \ #!/bin/sh : "This is a shell archive, meaning: " : "1. Remove everything above the #! /bin/sh line. " : "2. Save the rest in a file. " : "3. Execute the file with /bin/sh (not csh) to create the files:" : " README" : " explain" : " hdate.6" : " Makefile" : " hdate.h" : " hdate.c" : " hcal.c" : " hcom.c" : "This archive created: Sun Apr 5 08:52:41 EET 1987 " if [ -f README ] then echo file README exists else echo extracting file: README sed 's/^X//' >README << 'END-of-README' XThis is the 2nd distribution of hdate/hcal. It is still not Xportable anywhere, but at least the machine-dependent parts are Xconcentrated in one place. The main problem is that it needs X34-bit integers; if your machine can cope, just #define BIGLONG Xin hdate.h, otherwise re-write the routines 'muldiv' and X'mulmod'. The given version is for the VAX, plus a double f.p. Xworkaround for machines with enough precision. X XYou'll have to re-#define MINWEST to reflect your local time zone Xin minutes west of Greenwich; this is done to prevent DST from Xtaking effect (since Hebrew dates change at sunset), and to Xsimplify calculation (i.e. I was lazy). X XOther #define's are HEBREW to make a version that prints on a Xhebrew printer, and REV if that printer does not change direction Xwhen switching to Hebrew (that is, almost any printer I know of). XThe output is in lower-case letters, so you'll still need to pipe Xit through 'tr' or 'sed' to convert to what your printer expects. X(In my case it's sed 's/[`-z][`-z]*/^[n&^O/g'). X XThe files included herein are: README - this file, explain - an Xexplanation of the algorithm, hdate.6 - the man page (nroff/troff Xsource), Makefile, hdate.h - common definitions, hdate.c & hcal.c X- main source files of hdate & hcal, hcom.c - code common to Xboth. X X Good Luck, X Amos Shapir XApr. 1, 1987 - 2 Nisan 5747 END-of-README fi if [ -f explain ] then echo file explain exists else echo extracting file: explain sed 's/^X//' >explain << 'END-of-explain' XIf you wish to write your own hdate program, here are the Xprinciples. They are based on common knowledge and a book called X'the 6000-year calendar' by A. Akavia (I don't know if it was Xever translated from Hebrew). The given algorithm is the one used Xcurrently (others were used in different times in history), and Xis in use since about the 10th century A.D. X XThe calendar is lunisolar - each year starts close to the autumn Xequinox, but each month starts at the new moon. Dates change at Xsunset, so the 1st day of the month is the one following the new Xmoon. Months are alternately 30 and 29 days long, and are named: XTishrey, Heshvan, Kislev, Tevet, Shvat, Adar, Nisan, Iyar, Sivan, XTamuz, Av and Elul. (In biblical times, the year used to start at XNisan.) A standard year is therefore 354 days long. X XIn leap years, an extra month of 30 days is inserted before Adar X(which used to be the last month), and the 2 Adar's are called X'Adar a' and 'Adar b'. Leap years occur 7 times in a 19-year Xcycle, and are years 3, 6, 8, 11, 14, 17 and 19. A standard leap Xyear is 384 days long. X XFor various reasons of religious practices, the year cannot start Xon a Sunday, Wednesday or a Friday. If an adjustment is needed, a Xday is added to Heshvan or taken off Kislev. Thus a regular year Xmay be 353 to 355 days long, and a leap year - 383 to 385. X XNow, to the computation: each day starts at 6 p.m., and consists Xof 24 hours; the hour is sub-divided into 1080 'parts'. The month Xis 29 days, 12 hours and 793 parts long (let's use the notation X12h793). In all computations, we are only interested in the time Xof the new moon modulu one week; the starting point (1st new moon Xof year 1) is Monday, 5h204, i.e. 23:11:20. To give you a more Xrecent clue, year 5701 started (exactly 300 cycles later) on XThursday, Oct. 3, 1940. The new moon was on Wednesday, 2h504. X XGiven a date, find the number of days since a pre-calculated Xstarting point (preferably a new moon of Tishrey), then subtract Xcycles and years to arrive at the new moon of the requested year, Xand calculate its offset into the week. If one or more of the Xfollowing conditions are true, the new year is delayed by one (or X2, see below) days: X X1) The hour >= 18 (i.e. after noon); X2) Day is a Tuesday, and the hour >= 9h204 in a non-leap year; X3) Day is a Monday, and the hour >= 15h589 and the previous year X is a leap year; X4) Day is a Sunday, a Wednesday or a Friday. X X(Note that the delay because of 1-3 may cause another delay Xbecause of 4, and the new year would start 2 days after the new Xmoon. Rule 1 is to make sure the new moon is seen after sunset on Xthe first day; rules 2 and 3 are to prevent a double delay from Xcreating a 356- or 382-day long year.) X XNow do it again for the next year, and you have the exact length Xof the requested year, by which you can determine the lengths of XHeshvan, Kislev, and (by year number modulu 19, see above) Adar. XBy what's left of the day number in the year, you can find the Xexact date. X XI hope this is not too confusing! END-of-explain fi if [ -f hdate.6 ] then echo file hdate.6 exists else echo extracting file: hdate.6 sed 's/^X//' >hdate.6 << 'END-of-hdate.6' X.TH HDATE 6 "4 Dec 1985" X.UC 4 X.SH NAME Xhdate, hcal \- hebrew date and calendar X.SH SYNOPSIS X.B hdate X[ X.B \-j X] [ X.B \-h X] [ day month year ] X.PP X.B hcal X[ X.B \-j X] [ month ] year X.SH DESCRIPTION X.I Hdate Xtranslates the specified date to the Hebrew calendar. XIf no arguments are given, it uses today's date. X`month' is a number between 1 and 12. XIf `year' is less than 100, 1900 is added. X.PP XIf the X.B \-h Xoption is specified, X.I hdate Xtakes the given date as a hebrew date, and translates it Xback to the common calendar. XMonths are numbered with Tishrey being 1 and Elul 12; Adar2 in Xa leap year (me`ubereth) is specified by adding 30 to the day X(thus Purim is either 14/6 or 44/6). XNote that for dates in the 2nd half of the 20th century A.D., X`year' should be in the 5700's. X.PP X.I Hcal Xprints a calendar with both common and Hebrew dates for the Xspecified month, or the whole year if no month is specified. XBiblical holidays are marked by `*', other holidays by `+'. X.PP XIf the X.B \-j Xoption is specified the given or output common date is Xset in the (old) Julian, rather than the (current) Gregorian calendar. XThis option is default for years before 1582 (5342 Hebrew). XWith this option, 1900 is X.I not Xadded to years before 100. X.SH "SEE ALSO" Xdate(1), cal(1) X.SH AUTHOR XAmos Shapir, NSTA X.SH BUGS XThese programs use the currently held method of computing Xthe Hebrew calendar, which has been in use only since the 9th century. END-of-hdate.6 fi if [ -f Makefile ] then echo file Makefile exists else echo extracting file: Makefile sed 's/^X//' >Makefile << 'END-of-Makefile' XCFLAGS= -O Xall: hdate hcal X Xhdate: hdate.o hcom.o X cc -o hdate hdate.o hcom.o X Xhcal: hcal.o hcom.o X cc -o hcal hcal.o hcom.o X Xhdate.o hcal.o hcom.o: hdate.h X Xclean: X rm -f hdate.o hcal.o hcom.o END-of-Makefile fi if [ -f hdate.h ] then echo file hdate.h exists else echo extracting file: hdate.h sed 's/^X//' >hdate.h << 'END-of-hdate.h' X#define MINWEST (-120) /* minutes west of GMT */ X/* #define HEBREW /* output for Hebrew printer */ X/* #define REV /* " that is not bi-directional */ X/* #define BIGLONG /* if your longs >= 34 bits */ X struct hdate { X int hd_day; X int hd_mon; X int hd_year; X int hd_dw; X int hd_flg; X} *hdate(); X Xchar *mname[], *hmname[]; X#ifdef HEBREW X char *hnum(); X#ifdef REV Xchar *rev(); X#else X#define rev(s) s X#endif X#endif END-of-hdate.h fi if [ -f hdate.c ] then echo file hdate.c exists else echo extracting file: hdate.c sed 's/^X//' >hdate.c << 'END-of-hdate.c' X/* X | Hebrew dates since 2nd century A.D. X | by Amos Shapir 1978 (rev. 1985) X | The HEBREW option prints hebrew strings in l.c. X | flags: -j - input date is Julian (default before 1582 (5342)) X | -h - translate hebrew date to general X */ X#include "hdate.h" X Xint jflg, hflg; Xmain(argc, argv) X char **argv; X{ X register m, y, n; X long time(); X struct hdate *gdate(), *h; X#ifdef HEBREW X static hs[50]; X#endif X X while(argc>=2 && argv[1][0]=='-') { X switch(argv[1][1]) { X case 'j': X jflg++; break; X case 'h': X hflg++; break; X default: X usage(); X } X argv++; X argc--; X } X if(argc == 1) { X /* date changes at 1800 local time */ X n = (time(0)+(6*60-MINWEST)*60)/(24*60*60)+1; X m = 1; X y = 1970; X hflg = 0; X } else if(argc == 4) { X y = atoi(argv[3]); X if(y < 100 && !jflg && !hflg) X y += 1900; X if(hflg && y < 5342 || !hflg && y < 1582) X jflg++; X m = atoi(argv[2]); X n = atoi(argv[1]); X } else X usage(); X if(hflg) { X h = gdate(n, m, y); X printf("%d %s %d", h->hd_day+1, mname[h->hd_mon], h->hd_year); X if(jflg) X printf(" (J)"); X printf("\n"); X return 0; X } X h = hdate(n, m, y); X#ifdef HEBREW X strcat(hs, hnum(h->hd_day+1)); X strcat(hs, " "); X strcat(hs, hmname[h->hd_mon]); X strcat(hs, " "); X strcat(hs, hnum(h->hd_year)); X printf("%s\n", rev(hs)); X#else X printf("%d %s %d\n", h->hd_day+1, hmname[h->hd_mon], h->hd_year); X#endif X} Xusage() X{ X printf("Usage: hdate [-j] [-h] [day mon year]\n"); X exit(1); X} X X/* X | compute general date structure from hebrew date X */ Xstruct hdate * Xgdate(d, m, y) X register m, y, d; X{ X static struct hdate h; X register s; X X y -= 3744; X s = dysiz(y); X d += s; X s = dysiz(y+1)-s; /* length of year */ X d += (59*(m-1)+1)/2; /* regular months */ X /* special cases */ X if(s%10>4 && m>2) X d++; X if(s%10<4 && m>3) X d--; X if(s>365 && m>6) X d += 30; X d -= 6002; X if(!jflg) { /* compute century */ X y = (d+36525)*4/146097-1; X d -= y/4*146097+(y&3)*36524; X y *= 100; X } else { X d += 2; X y = 0; X } X /* compute year */ X s = (d+366)*4/1461-1; X d -= s/4*1461+(s&3)*365; X y += s; X /* compute month */ X m = (d+245)*12/367-7; X d -= m*367/12-30; X if(++m >= 12) { X m -= 12; X y++; X } X h.hd_day = d; X h.hd_mon = m; X h.hd_year = y; X return(&h); X} END-of-hdate.c fi if [ -f hcal.c ] then echo file hcal.c exists else echo extracting file: hcal.c sed 's/^X//' >hcal.c << 'END-of-hcal.c' X/* X | calendar with hebrew dates X | by Amos Shapir 1984 (rev. 1985) X | The HEBREW option prints hebrew strings in l.c. X | The REV option reverses them before printing. X | flag: -j - input date is Julian (default before 1582) X */ X#include "hdate.h" Xint jflg; Xmain(argc, argv) X char **argv; X{ X register m, y; X X if(argc>=2 && argv[1][0]=='-' && argv[1][1]=='j') { X jflg++; X argv++; X argc--; X } X if(argc == 2) X y = atoi(argv[1]); X else if(argc == 3) { X m = atoi(argv[1]); X y = atoi(argv[2]); X } else { X printf("Usage: hcal [-j] [mon] year\n"); X return (1); X } X if(y < 100 && !jflg) X y += 1900; X if(y < 1582) X jflg++; X if(argc == 2) X for(m=1; m<=12; m++) X hcal(m, y); X else X hcal(m, y); X return (0); X} X X/* bit array of holidays */ Xlong hagim[] = { X 0x408406, 0, 0, 0, 0, X 0, 0x208000, 0, 0x40, 0, X 0, 0, 0, 0 X}, Xmoadim[] = { X 0x3F0000, 0, 0x7E000000, 0x406, 0x8000, X 0xC000, 0x1F0000, 0x40000, 0, 0x20000, X 0x200, 0, 0, 0xC000 X}; X X/* print cal of month m of Gregorian year y */ Xhcal(m, y) X{ X register i, w, ms, hmsk, mmsk, hi; X struct hdate df, dl; X#ifdef HEBREW X static hs[50]; X#endif X X ms = 30+(0x15aa>>m&1); X if(m==2) { X ms = 28; X if((y&3)==0 && (jflg || !(y%100==0 && (y/100&3)!=0))) X ms++; X } X df = *hdate(1, m, y); X dl = *hdate(ms, m, y); X if(y >= 1948) /* 5 Iyar */ X moadim[7] |= 0x20; X if(dl.hd_flg < 0) /* Hanukah in a short year */ X moadim[3] |= 0x8; X printf("%s %4d", mname[m-1], y); X#ifdef HEBREW X strcpy(hs, hmname[df.hd_mon]); X if(df.hd_year != dl.hd_year) { X strcat(hs, " "); X strcat(hs, hnum(df.hd_year)); X } X if(df.hd_mon != dl.hd_mon) { X strcat(hs, " - "); X strcat(hs, hmname[dl.hd_mon]); X } X strcat(hs, " "); X strcat(hs, hnum(dl.hd_year)); X printf("%*s", 41-(strlen(mname[m-1])+5+strlen(hs)), ""); X printf("%s\n SUN MON TUE WED THU FRI SAT\n", rev(hs)); X#else X printf(" / %s", hmname[df.hd_mon]); X if(df.hd_year != dl.hd_year) X printf(" %d", df.hd_year); X if(df.hd_mon != dl.hd_mon) X printf(" - %s", hmname[dl.hd_mon]); X printf(" %d\n", dl.hd_year); X printf(" Sun Mon Tue Wed Thu Fri Sat\n"); X#endif X w = df.hd_dw; X for(i=0; i<w; i++) X printf(" "); X hi = df.hd_day+1; X hmsk = hagim[df.hd_mon]>>hi; X mmsk = moadim[df.hd_mon]>>hi; X for(i=1; i<=ms; i++) { X#ifdef HEBREW X printf("%2d%c%2s", i, hmsk&1 ? '*' : mmsk&1 ? '+' : '/', rev(hnum(hi))); X#else X printf("%2d%c%2d", i, hmsk&1 ? '*' : mmsk&1 ? '+' : '/', hi); X#endif X if(++w == 7) { X printf("\n"); X w = 0; X } else X printf(" "); X /* hebrew month changes */ X if(i==1 && ms==31 && dl.hd_day==0 && df.hd_day!=0) { X df = *hdate(2, m, y); X hi = 0; X hmsk = hagim[df.hd_mon]; X mmsk = moadim[df.hd_mon]; X } else if(i == ms-dl.hd_day-1) { X hi = 0; X hmsk = hagim[dl.hd_mon]; X mmsk = moadim[dl.hd_mon]; X } X hi++; X hmsk >>= 1; X mmsk >>= 1; X } X if(ms-w <= 28) X printf("\n"); X printf("\n\n"); X} END-of-hcal.c fi if [ -f hcom.c ] then echo file hcom.c exists else echo extracting file: hcom.c sed 's/^X//' >hcom.c << 'END-of-hcom.c' X/* routines common to hdate and hcal */ X X#include "hdate.h" X Xchar *mname[]= { X#ifdef HEBREW X "JANUARY", "FEBRUARY", "MARCH", "APRIL", X "MAY", "JUNE", "JULY", "AUGUST", X "SEPTEMBER", "OCTOBER", "NOVEMBER", "DECEMBER", X#else X "January", "February", "March", "April", X "May", "June", "July", "August", X "September", "October", "November", "December", X#endif X}; Xchar *hmname[] = { X#ifdef HEBREW X "zyxi", "gyeo", "kqle", X "haz", "yah", "`cx", X "piqo", "`ix", "qieo", X "znef", "`a", "`lel", X "`cx `", "`cx a" X#else X "Tishrey", "Heshvan", "Kislev", X "Tevet","Shvat", "Adar", X "Nisan", "Iyar", "Sivan", X "Tamuz", "Av", "Elul", X "Adar a", "Adar b" X#endif X}; X X X/* X | compute date structure from no. of days since 1 Tishrei 3744 X */ X/* constants, in 1/18th of minute */ X#define HOUR 1080 X#define DAY (24*HOUR) X#define WEEK (7*DAY) X#define M(d,h,p) (d*DAY+h*HOUR+p) X#define MONTH M(29,12,793) Xstruct hdate * Xhdate(d, m, y) X register m, y, d; X{ X static struct hdate h; X register s; X extern jflg; X X if((m -= 2) <= 0) { X m += 12; X y--; X } X /* no. of days, Julian calendar */ X d += 365*y+y/4+367*m/12+5968; X /* Gregorian calendar */ X if(!jflg) X d -= y/100-y/400-2; X h.hd_dw = (d+1)%7; X X /* compute the year */ X#ifdef BIGLONG X m = d*DAY/MONTH; /* needs ~34 bits */ X#else X m = muldiv(d, DAY, 0, MONTH); X#endif X y = (m+38)*19/235-3; X s = dysiz(y); X d -= s; X s = dysiz(y+1)-s; X y += 3744; X X h.hd_flg = s%10-4; X if(d < 0) { /* computed year was overestimated */ X d += s; X y--; X } else if(d >= s) { /* computed year was underestimated */ X d -= s; X y++; X } X X /* compute day and month */ X if(d >= s-236) { /* last 8 months are regular */ X d -= s-236; X m = d*2/59; X d -= (m*59+1)/2; X m += 4; X if(s>365 && m<=5) /* Adar of Meuberet */ X m += 8; X } else { X /* 1st 4 months have 117-119 days */ X s = 114+s%10; X m = d*4/s; X d -= (m*s+3)/4; X } X X h.hd_day = d; X h.hd_mon = m; X h.hd_year = y; X return(&h); X} X X/* no. of months in y years */ Xmsiz(y) X register y; X{ X return y*12+(y*7+1)/19; X} X X/* no. of days in y years */ Xdysiz(y) X register y; X{ X register m, nm, dw, s; X#ifdef BIGLONG X long nmt; /* needs ~34 bits */ X#endif X X m = msiz(y); X#ifdef BIGLONG X nmt = m*MONTH+M(0,1,779); /* molad at 197 cycles + M(2,5,204) */ X nm = nmt%WEEK; X s = nmt/DAY-2; X#else X nm = mulmod(m, MONTH, M(0,1,779), WEEK); X s = muldiv(m, MONTH, M(0,1,779), DAY)-2; X#endif X dw = nm/DAY; X nm %= DAY; X /* special cases of Molad Zaken */ X if(nm >= 18*HOUR || msiz(y+1)-m==12 && dw==3 && nm>=M(0,9,204) || X m-msiz(y-1)==13 && dw==2 && nm>=M(0,15,589)) X s++,dw++; X /* ADU */ X if(dw == 1 || dw == 4 || dw == 6) X s++; X return s; X} X X#ifndef BIGLONG X/* (a*b+c)/m where a*b might overflow */ Xmuldiv (a, b, c, m) Xint a, b, c, m; X{ X#ifdef vax X asm ("emul 4(ap),8(ap),12(ap),r0"); X asm ("ediv 16(ap),r0,r0,r2"); X#else X /* if double is large enough */ X double floor(); X return (long)floor(((double)a*b+c)/m); X#endif X} X X/* (a*b+c)%m where a*b might overflow */ Xmulmod (a, b, c, m) Xint a, b, c, m; X{ X#ifdef vax X asm ("emul 4(ap),8(ap),12(ap),r0"); X asm ("ediv 16(ap),r0,r2,r0"); X#else X /* if double is large enough */ X double floor(), x; X x = (double)a*b+c; X return (long)(x-floor(x/m)*m); X#endif X} X#endif X X#ifdef HEBREW Xchar * Xhnum(n) X register n; X{ X static char hn[16]; X register char *p = hn; X X if(n >= 1000) { X (void)hnum(n/1000); /* result in hn */ X while(*p) X p++; X n %= 1000; X } X while(n >= 400) { X *p++ = 'z'; X n -= 400; X } X if(n >= 100) { X *p++ = 'v'+n/100; X n %= 100; X } X if(n >= 10) { X if(n == 15 || n == 16) X n -= 9; X *p++ = "hiklnpqrtv"[n/10]; X n %= 10; X } X if(n > 0) X *p++ = '_'+n; X *p++ = 0; X return hn; X} X#ifdef REV Xchar * Xrev(as) X char *as; X{ X register char *p, *s; X register t; X X s = as; X for(p=s;*p;p++); X while(p > s) { X t = *--p; X *p = *s; X *s++ = t; X } X return as; X} X#endif X#endif END-of-hcom.c fi exit 0 o / o / o / o / -----------------X---------------X---------------X---------------X---- o \ o \ o \ o \ May the Source be with you, always... -- Amos Shapir National Semiconductor (Israel) 6 Maskit st. P.O.B. 3007, Herzlia 46104, Israel Tel. (972)52-522261 amos%nsta@nsc.com {hplabs,pyramid,sun,decwrl} 34.48'E 32.10'N