mirror of
https://salsa.debian.org/dskoll/remind.git
synced 2026-04-17 14:59:20 +02:00
545 lines
17 KiB
C
545 lines
17 KiB
C
/***************************************************************/
|
|
/* */
|
|
/* HBCAL.C */
|
|
/* */
|
|
/* Support for the Hebrew calendar */
|
|
/* */
|
|
/* This file is part of REMIND. */
|
|
/* Copyright (C) 1992-1996 by David F. Skoll */
|
|
/* */
|
|
/* Derived from code written by Amos Shapir in 1978; revised */
|
|
/* 1985. */
|
|
/* */
|
|
/***************************************************************/
|
|
|
|
static char const RCSID[] = "$Id: hbcal.c,v 1.1 1996-03-27 03:25:58 dfs Exp $";
|
|
|
|
#include <stdio.h> /* For FILE used by protos.h - sigh. */
|
|
#include "config.h"
|
|
#include "types.h"
|
|
#include "protos.h"
|
|
#include "globals.h"
|
|
#include "err.h"
|
|
#define HOUR 1080L
|
|
#define DAY (24L*HOUR)
|
|
#define WEEK (7L*DAY)
|
|
#define M(h,p) ((long)(h*HOUR+p))
|
|
#define MONTH (DAY+M(12,793))
|
|
|
|
/* Correction to convert base reference to 1990. NOTE: If you change
|
|
the value of BASE in config.h, this will NOT WORK! You'll have to
|
|
add the appropriate number of days to CORRECTION. */
|
|
|
|
#define CORRECTION 732774L
|
|
|
|
#define TISHREY 0
|
|
#define HESHVAN 1
|
|
#define KISLEV 2
|
|
#define TEVET 3
|
|
#define SHVAT 4
|
|
#define ADARA 5
|
|
#define ADARB 6
|
|
#define NISAN 7
|
|
#define IYAR 8
|
|
#define SIVAN 9
|
|
#define TAMUZ 10
|
|
#define AV 11
|
|
#define ELUL 12
|
|
#define ADAR 13
|
|
|
|
#define JAHR_NONE 0
|
|
#define JAHR_FORWARD 1
|
|
#define JAHR_BACKWARD 2
|
|
|
|
#define ADAR2ADARB 0
|
|
#define ADAR2ADARA 1
|
|
#define ADAR2BOTH 2
|
|
|
|
static char *HebMonthNames[] = {
|
|
"Tishrey", "Heshvan", "Kislev", "Tevet", "Shvat", "Adar A", "Adar B",
|
|
"Nisan", "Iyar", "Sivan", "Tamuz", "Av", "Elul", "Adar"};
|
|
|
|
static char MaxMonLen[] = {
|
|
30, 30, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29, 29};
|
|
|
|
static char HebIsLeap[] = {0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,1};
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* RoshHashana */
|
|
/* */
|
|
/* Return the Julian date for Rosh Hashana of specified */
|
|
/* Hebrew year. (ie, 5751, not 1990) */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC int RoshHashana(int i)
|
|
#else
|
|
int RoshHashana(i)
|
|
int i;
|
|
#endif
|
|
{
|
|
long j;
|
|
j = DaysToHebYear(i-3744) - CORRECTION;
|
|
return (int) j; /* No overflow check... very trusting! */
|
|
}
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* DaysToHebYear */
|
|
/* */
|
|
/* Return the number of days to RH of specified Hebrew year */
|
|
/* from new moon before Tishrey 1 5701. */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC long DaysToHebYear(int y)
|
|
#else
|
|
long DaysToHebYear(y)
|
|
int y;
|
|
#endif
|
|
{
|
|
long m, nm, dw, s, l;
|
|
|
|
l = y*7+1; /* no. of leap months */
|
|
m = y*12+l/19; /* total no. of months */
|
|
nm = m*MONTH+M(1,779); /* molad at 197 cycles */
|
|
s = m*28+nm/DAY-2;
|
|
|
|
nm %= WEEK;
|
|
l %= 19L;
|
|
dw = nm/DAY;
|
|
nm %= DAY;
|
|
|
|
/* special cases of Molad Zaken */
|
|
if (nm >= 18*HOUR ||
|
|
(l < 12 && dw==3 && nm>=M(9,204)) ||
|
|
(l < 7 && dw==2 && nm>=M(15,589)))
|
|
s++,dw++;
|
|
/* ADU */
|
|
if(dw == 1 || dw == 4 || dw == 6)
|
|
s++;
|
|
return s;
|
|
}
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* DaysInHebYear */
|
|
/* */
|
|
/* Return the number of days in the Hebrew year. */
|
|
/* */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC int DaysInHebYear(int y)
|
|
#else
|
|
int DaysInHebYear(y)
|
|
int y;
|
|
#endif
|
|
{
|
|
long thisyear, nextyear;
|
|
|
|
thisyear = DaysToHebYear(y-3744);
|
|
nextyear = DaysToHebYear(y-3743);
|
|
return (int) (nextyear - thisyear);
|
|
}
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* DaysInHebMonths */
|
|
/* */
|
|
/* Return a pointer to an array giving lengths of months */
|
|
/* given the LENGTH of the Hebrew year. */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC char *DaysInHebMonths(int ylen)
|
|
#else
|
|
char *DaysInHebMonths(ylen)
|
|
int ylen;
|
|
#endif
|
|
{
|
|
static char monlen[13] =
|
|
{30, 29, 30, 29, 30, 0, 29, 30, 29, 30, 29, 30, 29};
|
|
|
|
|
|
if (ylen > 355) {
|
|
monlen[ADARA] = 30;
|
|
ylen -= 30;
|
|
} else monlen[ADARA] = 0;
|
|
|
|
if (ylen == 353) monlen[KISLEV] = 29; else monlen[KISLEV] = 30;
|
|
if (ylen == 355) monlen[HESHVAN] = 30; else monlen[HESHVAN] = 29;
|
|
|
|
return monlen;
|
|
}
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* HebToJul */
|
|
/* */
|
|
/* Convert a Hebrew date to Julian. */
|
|
/* Hebrew months range from 0-12, but Adar A has 0 length in */
|
|
/* non-leap-years. */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC int HebToJul(int hy, int hm, int hd)
|
|
#else
|
|
int HebToJul(hy, hm, hd)
|
|
int hy, hm, hd;
|
|
#endif
|
|
{
|
|
int ylen;
|
|
char *monlens;
|
|
int rh;
|
|
int m;
|
|
|
|
/* Do some range checking */
|
|
if (hy - 3761 < BASE || hy - 3760 > BASE+YR_RANGE) return -1;
|
|
|
|
ylen = DaysInHebYear(hy);
|
|
monlens = DaysInHebMonths(ylen);
|
|
|
|
/* Get the Rosh Hashana of the year */
|
|
rh = RoshHashana(hy);
|
|
|
|
/* Bump up to the appropriate month */
|
|
for (m=0; m<hm; m++) rh += monlens[m];
|
|
|
|
/* Add in appropriate number of days */
|
|
rh += hd - 1;
|
|
return rh;
|
|
}
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* JulToHeb */
|
|
/* */
|
|
/* Convert a Julian date to Hebrew. */
|
|
/* Hebrew months range from 0-12, but Adar A has 0 length in */
|
|
/* non-leap-years. */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC void JulToHeb(int jul, int *hy, int *hm, int *hd)
|
|
#else
|
|
void JulToHeb(jul, hy, hm, hd)
|
|
int jul, *hy, *hm, *hd;
|
|
#endif
|
|
{
|
|
int y, m, d;
|
|
int rh;
|
|
int ylen;
|
|
char *monlen;
|
|
/* Get the common year */
|
|
FromJulian(jul, &y, &m, &d);
|
|
y += 3763; /* Over-estimate a bit to be on the safe side below... */
|
|
|
|
/* Find the RH just before desired date */
|
|
while ((rh=RoshHashana(y))>jul) y--;
|
|
|
|
/* Got the year - now find the month */
|
|
jul -= rh;
|
|
ylen = DaysInHebYear(y);
|
|
monlen = DaysInHebMonths(ylen);
|
|
m = 0;
|
|
while((jul >= monlen[m]) || !monlen[m]) {
|
|
jul -= monlen[m];
|
|
m++;
|
|
}
|
|
|
|
*hy = y;
|
|
*hm = m;
|
|
*hd = jul+1;
|
|
}
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* HebNameToNum */
|
|
/* */
|
|
/* Convert a Hebrew month's name to its number, given the */
|
|
/* year. */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC int HebNameToNum(const char *mname)
|
|
#else
|
|
int HebNameToNum(mname)
|
|
char *mname;
|
|
#endif
|
|
{
|
|
int i;
|
|
int m=-1;
|
|
|
|
for (i=0; i<14; i++)
|
|
if (!StrCmpi(mname, HebMonthNames[i])) {
|
|
m = i;
|
|
break;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* HebMonthname */
|
|
/* */
|
|
/* Convert a Hebrew month's number to its name, given the */
|
|
/* year. */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC char *HebMonthName(int m, int y)
|
|
#else
|
|
char *HebMonthName(m, y)
|
|
int m, y;
|
|
#endif
|
|
{
|
|
if (m != ADARA && m != ADARB) return HebMonthNames[m];
|
|
|
|
if (!HebIsLeap[(y-1)%19]) return HebMonthNames[ADAR];
|
|
else return HebMonthNames[m];
|
|
}
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* GetValidHebDate */
|
|
/* */
|
|
/* Given the day of a month, a Hebrew month number, and a */
|
|
/* year, return a valid year number, month number, and day */
|
|
/* number. Returns 0 for success, non-0 for failure. */
|
|
/* If *dout is set to -1, then date is completely invalid. */
|
|
/* Otherwise, date is only invalid in specified year. */
|
|
/* */
|
|
/* Algorithm: */
|
|
/* - Convert references to Adar to Adar B. */
|
|
/* If jahr == 0 then */
|
|
/* - If no such date in current Hebrew year, return */
|
|
/* failure. */
|
|
/* else follow jahrzeit rules: */
|
|
/* - If jahr == 1: Convert 30 Kislev to 1 Tevet and */
|
|
/* 30 Heshvan to 1 Kislev if chaser. */
|
|
/* Convert 30 Adar A to 1 Nisan in nonleap */
|
|
/* This rule is NOT appropriate for a */
|
|
/* jahrzeit on 30 Adar A. Use rule 2 for */
|
|
/* that. However, I believe it is correct */
|
|
/* for smachot. */
|
|
/* - If jahr == 2: Convert 30 Kislev to 29 Kislev and */
|
|
/* 30 Heshvan to 29 Heshvan if chaser. */
|
|
/* Change 30 Adar A to 30 Shvat in nonleap */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC int GetValidHebDate(int yin, int min, int din, int adarbehave,
|
|
int *mout, int *dout, int jahr)
|
|
#else
|
|
int GetValidHebDate(yin, min, din, adarbehave, mout, dout, jahr)
|
|
int yin, min, din, adarbehave, *mout, *dout, jahr;
|
|
#endif
|
|
{
|
|
char *monlen;
|
|
int ylen;
|
|
|
|
*mout = min;
|
|
*dout = din;
|
|
|
|
/* Do some error checking */
|
|
if (din < 1 || din > MaxMonLen[min] || min < 0 || min > 13) {
|
|
*dout = -1;
|
|
return E_BAD_HEBDATE;
|
|
}
|
|
|
|
ylen = DaysInHebYear(yin);
|
|
monlen = DaysInHebMonths(ylen);
|
|
|
|
/* Convert ADAR as necessary */
|
|
if (min == ADAR) {
|
|
switch(adarbehave) {
|
|
case ADAR2ADARA: if (monlen[ADARA]) *mout = min = ADARA;
|
|
else *mout = min = ADARB;
|
|
break;
|
|
|
|
case ADAR2ADARB: *mout = min = ADARB; break;
|
|
|
|
default:
|
|
Eprint("GetValidHebDate: Bad adarbehave value %d", adarbehave);
|
|
return E_SWERR;
|
|
}
|
|
}
|
|
|
|
if (din <= monlen[min]) return OK;
|
|
|
|
switch(jahr) {
|
|
case JAHR_NONE: return E_BAD_DATE;
|
|
|
|
case JAHR_FORWARD:
|
|
if (min == KISLEV) {
|
|
*mout = TEVET;
|
|
*dout = 1;
|
|
return OK;
|
|
} else if (min == HESHVAN) {
|
|
*mout = KISLEV;
|
|
*dout = 1;
|
|
return OK;
|
|
} else if (min == ADARA) {
|
|
if (din > 29) {
|
|
*dout = 1;
|
|
*mout = NISAN;
|
|
} else {
|
|
*dout = din;
|
|
*mout = ADARB;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
Eprint("GetValidHebDate: (1) software error! %d", jahr);
|
|
return E_SWERR;
|
|
|
|
case JAHR_BACKWARD:
|
|
if (min == KISLEV) {
|
|
*mout = KISLEV;
|
|
*dout = 29;
|
|
return OK;
|
|
} else if (min == HESHVAN) {
|
|
*mout = HESHVAN;
|
|
*dout = 29;
|
|
return OK;
|
|
} else if (min == ADARA) {
|
|
if (din > 29) {
|
|
*dout = 30;
|
|
*mout = SHVAT;
|
|
} else {
|
|
*mout = ADARB;
|
|
*dout = din;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
Eprint("GetValidHebDate: (2) software error! %d", jahr);
|
|
return E_SWERR;
|
|
|
|
default:
|
|
Eprint("GetValidHebDate: (3) software error! %d", jahr);
|
|
return E_SWERR;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* GetNextHebrewDate */
|
|
/* */
|
|
/* Get the next Hebrew date on or after specified date. */
|
|
/* */
|
|
/* Returns 0 for success, non-zero for failure. */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC int GetNextHebrewDate(int julstart, int hm, int hd,
|
|
int jahr, int adarbehave, int *ans)
|
|
#else
|
|
int GetNextHebrewDate(julstart, hm, hd, jahr, adarbehave, ans)
|
|
int julstart, hm, hd, jahr, adarbehave, *ans;
|
|
#endif
|
|
{
|
|
int r, yout, mout, dout, jul=1;
|
|
int adarflag = adarbehave;
|
|
|
|
/* I initialize jul above to stop gcc from complaining about
|
|
possible use of uninitialized variable. You can take it
|
|
out if the small inefficiency really bothers you. */
|
|
|
|
/* If adarbehave == ADAR2BOTH, set adarflag to ADAR2ADARA for now */
|
|
if (adarbehave == ADAR2BOTH) adarflag = ADAR2ADARA;
|
|
|
|
JulToHeb(julstart, &yout, &mout, &dout);
|
|
|
|
r = 1;
|
|
while(r) {
|
|
r = GetValidHebDate(yout, hm, hd, adarflag, &mout, &dout, jahr);
|
|
if (dout == -1) return r;
|
|
if (r) {
|
|
if (adarbehave == ADAR2BOTH && hm == ADAR) {
|
|
if (adarflag == ADAR2ADARA) {
|
|
adarflag = ADAR2ADARB;
|
|
} else {
|
|
adarflag = ADAR2ADARA;
|
|
yout++;
|
|
}
|
|
} else yout++;
|
|
continue;
|
|
}
|
|
jul = HebToJul(yout, mout, dout);
|
|
if (jul < 0) return E_DATE_OVER;
|
|
if (jul >= julstart) break;
|
|
else {
|
|
if (adarbehave == ADAR2BOTH && hm == ADAR) {
|
|
if (adarflag == ADAR2ADARA) {
|
|
adarflag = ADAR2ADARB;
|
|
} else {
|
|
adarflag = ADAR2ADARA;
|
|
yout++;
|
|
}
|
|
} else yout++;
|
|
r=1; /* Force loop to continue */
|
|
}
|
|
}
|
|
*ans = jul;
|
|
return OK;
|
|
}
|
|
|
|
/***************************************************************/
|
|
/* */
|
|
/* ComputeJahr */
|
|
/* */
|
|
/* Given a date of death, compute the value to use for jahr. */
|
|
/* */
|
|
/***************************************************************/
|
|
#ifdef HAVE_PROTOS
|
|
PUBLIC int ComputeJahr(int y, int m, int d, int *ans)
|
|
#else
|
|
int ComputeJahr(y, m, d, ans)
|
|
int y, m, d, *ans;
|
|
#endif
|
|
{
|
|
char *monlen;
|
|
int len;
|
|
|
|
*ans = JAHR_NONE;
|
|
|
|
len = DaysInHebYear(y);
|
|
monlen = DaysInHebMonths(len);
|
|
|
|
/* Check for Adar A */
|
|
if (m == ADARA && monlen[m] == 0) {
|
|
Eprint("No Adar A in %d", y);
|
|
return E_BAD_HEBDATE;
|
|
}
|
|
|
|
|
|
if (d < 1 || d > MaxMonLen[m] || m < 0 || m > 13) {
|
|
return E_BAD_HEBDATE;
|
|
}
|
|
|
|
if (d > monlen[m]) {
|
|
Eprint("%d %s %d: %s", d, HebMonthNames[m], y, ErrMsg[E_BAD_HEBDATE]);
|
|
return E_BAD_HEBDATE;
|
|
}
|
|
|
|
/* If the jahrzeit was in Adar A, we always use JAHR_BACKWARD */
|
|
if (m == ADARA) {
|
|
*ans = JAHR_BACKWARD;
|
|
return OK;
|
|
}
|
|
|
|
/* Get lengths of months in year following jahrzeit */
|
|
len = DaysInHebYear(y+1);
|
|
monlen = DaysInHebMonths(len);
|
|
|
|
if (d > monlen[m]) *ans = JAHR_FORWARD;
|
|
else *ans = JAHR_BACKWARD;
|
|
|
|
return OK;
|
|
}
|