[net.sources] Hebrew <--> Gregorian date conversion

amos@instable.UUCP (02/26/87)

Following is the C version of hdate.c, a program to convert to/from
Hebrew dates, with a man page, and hcal.c, a cal-like program
that prints a Hebrew/Gregorian calendar.

This program was written on a VAX, so it assumes 32-bit 'int's (actually
it needs at least 36 bits, but I worked around it using 'double').
If your machine has smaller 'int's, you'll have to change some of them
to 'long's (sorry, I never checked which).

The HEBREW option is to print the output on a Hebrew printer; the REV
option is for such printers that don't change print direction when switching
to Hebrew font (i.e., almost all of them).

A fortran-77 (and bc!) version is also avilable, but the user interface is
much uglier.
	Good Luck!

		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:"
: "	hdate.6"
: "	hdate.c"
: "	hcal.c"
: "This archive created:  Wed Feb 25 08:22:50 EET 1987 "
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 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
Xchar  *mname[] = {
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	"Tishrei", "Heshvan", "Kislev",
X	"Tevet","Shvat", "Adar",
X	"Nisan", "Iyar", "Sivan",
X	"Tamuz", "Av", "Elul",
X	"Adar a", "Adar b"
X#endif
X};
Xchar  *gmname[] = {
X	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
X	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X};
Xstruct hd {
X	int hd_day;
X	int hd_mon;
X	int hd_year;
X};
X
X#ifdef HEBREW
Xputh(n)
X	register n;
X{
X	char as[16];
X	register char *s;
X
X	s = as;
X	if(n >= 1000) {
X		puth(n/1000);
X		n %= 1000;
X	}
X	while(n >= 400) {
X		*s++ = 'z';
X		n -= 400;
X	}
X	if(n >= 100) {
X		*s++ = 'v'+n/100;
X		n %= 100;
X	}
X	if(n >= 10) {
X		if(n == 15 || n == 16)
X			n -= 9;
X		*s++ = "hiklnpqrtv"[n/10];
X		n %= 10;
X	}
X	if(n > 0)
X		*s++ = '_'+n;
X	/* gershayim */
X	if(s == &as[1])
X		*s++ = '\'';
X	else {
X		*s = s[-1];
X		s[-1] = '"';
X		/* sofyot */
X		if(*s=='k' || *s=='n' || *s=='p' || *s=='t' || *s=='v')
X			(*s)--;
X		++s;
X	}
X	*s++ = '\0';
X	printf(as);
X}
X#endif
X
Xint jflg, hflg;
Xmain(c, v)
X	char **v;
X{
X	register m, y, n;
X	long d, time();
X	struct tm *localtime(), *t;
X	struct hd *hdate(), *gdate(), *h;
X
X	while(c>=2 && v[1][0]=='-') {
X		switch(v[1][1]) {
X		case 'j':
X			jflg++; break;
X		case 'h':
X			hflg++; break;
X		default:
X			usage();
X		}
X		v++;
X		c--;
X	}
X	if(c == 1) {
X		/* date changes at 1800 Jerusalem time (1540 GMT) */
X		n = (time(0)+500*60)/(24*60*60)+1;
X		m = 1;
X		y = 1970;
X		hflg = 0;
X	} else if(c == 4) {
X		y = atoi(v[3]);
X		if(y < 100 && !jflg && !hflg)
X			y += 1900;
X		if(hflg && y < 5342 || !hflg && y < 1582)
X			jflg++;
X		m = atoi(v[2]);
X		n = atoi(v[1]);
X	} else
X		usage();
X	if(hflg) {
X		h = gdate(n, m, y);
X		printf("%d %s %d", h->hd_day+1, gmname[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	puth(h->hd_day+1);
X	printf(" %s ", mname[h->hd_mon]);
X	puth(h->hd_year);
X	printf("\n");
X#else
X	printf("%d %s %d\n", h->hd_day+1, mname[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 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 hd *
Xhdate(d, m, y)
X	register m, y, d;
X{
X	static struct hd h;
X	register s;
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
X	/* compute the year */
X	m = (double)d*DAY/MONTH;	/* needs ~34 bits */
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	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/*
X | compute general date structure from hebrew date
X */
Xstruct hd *
Xgdate(d, m, y)
X	register m, y, d;
X{
X	static struct hd 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}
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	double nmt;
X
X	m = msiz(y);
X	nmt = (double)m*MONTH+M(0,1,779); /* molad at 197 cycles + M(2,5,204) */
X	nm = nmt-(int)(nmt/WEEK)*WEEK;	/* actually nmt%WEEK */
X	s = (int)(nmt/DAY)-2;
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}
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
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
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/* 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
Xstruct hdate {
X	int hd_day;
X	int hd_mon;
X	int hd_year;
X	int hd_dw;
X	int hd_flg;
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, *hdate();
X#ifdef HEBREW
X	static hs[50];
X	char *hnum(), *rev();
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%s", 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}
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
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	m = (double)d*DAY/MONTH;	/* needs ~34 bits */
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	double nmt;
X
X	m = msiz(y);
X	nmt = (double)m*MONTH+M(0,1,779); /* molad at 197 cycles + M(2,5,204) */
X	nm = nmt-(int)(nmt/WEEK)*WEEK;	/* actually nmt%WEEK */
X	s = (int)(nmt/DAY)-2;
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#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 && *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	if(p-hn < 2)
X		*p++ = ' ';
X	*p++ = 0;
X	return hn;
X}
Xchar *
Xrev(as)
X	char *as;
X{
X#ifdef REV
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#endif
X	return as;
X}
X#endif
END-of-hcal.c
fi
exit

		o /		o /		o /		o /
-----------------X---------------X---------------X---------------X----
		o \		o \		o \		o \

-- 
	Amos Shapir
National Semiconductor (Israel)
6 Maskit st. P.O.B. 3007, Herzlia 46104, Israel
(011-972) 52-522261  amos%nsta@nsc.com 34.48'E 32.10'N