home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume37 / remind / patch05c / hbcal.c
Encoding:
C/C++ Source or Header  |  1993-04-27  |  15.7 KB  |  504 lines

  1. /***************************************************************/
  2. /*                                                             */
  3. /*  HBCAL.C                                                    */
  4. /*                                                             */
  5. /*  Support for the Hebrew calendar                            */
  6. /*                                                             */
  7. /*  This file is part of REMIND.                               */
  8. /*  Copyright (C) 1992, 1993 by David F. Skoll.                */
  9. /*                                                             */
  10. /*  Derived from code written by Amos Shapir in 1978; revised  */
  11. /*  1985.                                                      */
  12. /*                                                             */
  13. /***************************************************************/
  14. #include <stdio.h>  /* For FILE used by protos.h - sigh. */
  15. #include "config.h"
  16. #include "types.h"
  17. #include "protos.h"
  18. #include "globals.h"
  19. #include "err.h"
  20. #define HOUR 1080L
  21. #define DAY  (24L*HOUR)
  22. #define WEEK (7L*DAY)
  23. #define M(h,p) ((long)(h*HOUR+p))
  24. #define MONTH (DAY+M(12,793))
  25.  
  26. /* Correction to convert base reference to 1990.  NOTE:  If you change
  27.    the value of BASE in config.h, this will NOT WORK!  You'll have to
  28.    add the appropriate number of days to CORRECTION. */
  29.  
  30. #define CORRECTION 732774L
  31.  
  32. #define TISHREY 0
  33. #define HESHVAN 1
  34. #define KISLEV  2
  35. #define TEVET   3
  36. #define SHVAT   4
  37. #define ADARA   5
  38. #define ADARB   6
  39. #define NISAN   7
  40. #define IYAR    8
  41. #define SIVAN   9
  42. #define TAMUZ  10
  43. #define AV     11
  44. #define ELUL   12
  45. #define ADAR   13
  46.  
  47. #define JAHR_NONE     0
  48. #define JAHR_FORWARD  1
  49. #define JAHR_BACKWARD 2
  50.  
  51. static char *HebMonthNames[] = {
  52.    "Tishrey", "Heshvan", "Kislev", "Tevet", "Shvat", "Adar A", "Adar B",
  53.    "Nisan", "Iyar", "Sivan", "Tamuz", "Av", "Elul", "Adar"};
  54.  
  55. static char MaxMonLen[] = {
  56.    30, 30, 30, 29, 30, 30, 29, 30, 29, 30, 29, 30, 29, 29};
  57.  
  58. static char HebIsLeap[] = {0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,1};
  59.   
  60. /***************************************************************/
  61. /*                                                             */
  62. /*  RoshHashana                                                */
  63. /*                                                             */
  64. /*  Return the Julian date for Rosh Hashana of specified       */
  65. /*  Hebrew year.  (ie, 5751, not 1990)                         */
  66. /*                                                             */
  67. /***************************************************************/
  68. #ifdef HAVE_PROTOS
  69. PUBLIC int RoshHashana(int i)
  70. #else
  71. int RoshHashana(i)
  72. int i;
  73. #endif     
  74. {
  75.     long j;
  76.     j = DaysToHebYear(i-3744) - CORRECTION;
  77.     return (int) j; /* No overflow check... very trusting! */
  78. }
  79.  
  80. /***************************************************************/
  81. /*                                                             */
  82. /*  DaysToHebYear                                              */
  83. /*                                                             */
  84. /*  Return the number of days to RH of specified Hebrew year   */
  85. /*  from new moon before Tishrey 1 5701.                       */
  86. /*                                                             */
  87. /***************************************************************/
  88. #ifdef HAVE_PROTOS
  89. PUBLIC long DaysToHebYear(int y)
  90. #else
  91. long DaysToHebYear(y)
  92. int y;
  93. #endif
  94. {
  95.     long m, nm, dw, s, l;
  96.  
  97.     l = y*7+1;      /* no. of leap months */
  98.     m = y*12+l/19;  /* total no. of months */
  99.     nm = m*MONTH+M(1,779); /* molad at 197 cycles */
  100.     s = m*28+nm/DAY-2;
  101.  
  102.     nm %= WEEK;
  103.     l %= 19L;
  104.     dw = nm/DAY;
  105.     nm %= DAY;
  106.  
  107.     /* special cases of Molad Zaken */
  108.     if (nm >= 18*HOUR ||
  109.               (l < 12 && dw==3 && nm>=M(9,204)) ||
  110.           (l <  7 && dw==2 && nm>=M(15,589)))
  111.         s++,dw++;
  112.     /* ADU */
  113.     if(dw == 1 || dw == 4 || dw == 6)
  114.         s++;
  115.     return s;
  116. }
  117.  
  118. /***************************************************************/
  119. /*                                                             */
  120. /*  DaysInHebYear                                              */
  121. /*                                                             */
  122. /*  Return the number of days in the Hebrew year.              */
  123. /*                                                             */
  124. /*                                                             */
  125. /***************************************************************/
  126. #ifdef HAVE_PROTOS
  127. PUBLIC int DaysInHebYear(int y)
  128. #else
  129. int DaysInHebYear(y)
  130. int y;
  131. #endif
  132. {
  133.    long thisyear, nextyear;
  134.  
  135.    thisyear = DaysToHebYear(y-3744);
  136.    nextyear = DaysToHebYear(y-3743);
  137.    return (int) (nextyear - thisyear);
  138. }
  139.  
  140. /***************************************************************/
  141. /*                                                             */
  142. /*  DaysInHebMonths                                            */
  143. /*                                                             */
  144. /*  Return a pointer to an array giving lengths of months      */
  145. /*  given the LENGTH of the Hebrew year.                       */
  146. /*                                                             */
  147. /***************************************************************/
  148. #ifdef HAVE_PROTOS
  149. PUBLIC char *DaysInHebMonths(int ylen)
  150. #else
  151. char *DaysInHebMonths(ylen)
  152. int ylen;
  153. #endif
  154. {
  155.    static char monlen[13] =
  156.       {30, 29, 30, 29, 30, 0, 29, 30, 29, 30, 29, 30, 29};
  157.  
  158.  
  159.    if (ylen > 355) {
  160.       monlen[ADARA] = 30;
  161.       ylen -= 30;
  162.    } else monlen[ADARA] = 0;
  163.  
  164.    if (ylen == 353) monlen[KISLEV] = 29; else monlen[KISLEV] = 30;
  165.    if (ylen == 355) monlen[HESHVAN] = 30; else monlen[HESHVAN] = 29;
  166.  
  167.    return monlen;
  168. }
  169.  
  170. /***************************************************************/
  171. /*                                                             */
  172. /*  HebToJul                                                   */
  173. /*                                                             */
  174. /*  Convert a Hebrew date to Julian.                           */
  175. /*  Hebrew months range from 0-12, but Adar A has 0 length in  */
  176. /*  non-leap-years.                                            */
  177. /*                                                             */
  178. /***************************************************************/
  179. #ifdef HAVE_PROTOS
  180. PUBLIC int HebToJul(int hy, int hm, int hd)
  181. #else
  182. int HebToJul(hy, hm, hd)
  183. int hy, hm, hd;
  184. #endif
  185. {
  186.    int ylen;
  187.    char *monlens;
  188.    int rh;
  189.    int m;
  190.  
  191.    /* Do some range checking */
  192.    if (hy - 3761 < BASE || hy - 3760 > BASE+YR_RANGE) return -1;
  193.  
  194.    ylen = DaysInHebYear(hy);
  195.    monlens = DaysInHebMonths(ylen);
  196.  
  197.    /* Get the Rosh Hashana of the year */
  198.    rh = RoshHashana(hy);
  199.  
  200.    /* Bump up to the appropriate month */
  201.    for (m=0; m<hm; m++) rh += monlens[m];
  202.  
  203.    /* Add in appropriate number of days */
  204.    rh += hd - 1;
  205.    return rh;
  206. }
  207.  
  208. /***************************************************************/
  209. /*                                                             */
  210. /*  JulToHeb                                                   */
  211. /*                                                             */
  212. /*  Convert a Julian date to Hebrew.                           */
  213. /*  Hebrew months range from 0-12, but Adar A has 0 length in  */
  214. /*  non-leap-years.                                            */
  215. /*                                                             */
  216. /***************************************************************/
  217. #ifdef HAVE_PROTOS
  218. PUBLIC void JulToHeb(int jul, int *hy, int *hm, int *hd)
  219. #else
  220. void JulToHeb(jul, hy, hm, hd)
  221. int jul, *hy, *hm, *hd;
  222. #endif
  223. {
  224.    int y, m, d;
  225.    int rh;
  226.    int ylen;
  227.    char *monlen;
  228.    /* Get the common year */
  229.    FromJulian(jul, &y, &m, &d);
  230.    y += 3763; /* Over-estimate a bit to be on the safe side below... */
  231.  
  232.    /* Find the RH just before desired date */
  233.    while ((rh=RoshHashana(y))>jul) y--;
  234.  
  235.    /* Got the year - now find the month */
  236.    jul -= rh;
  237.    ylen = DaysInHebYear(y);
  238.    monlen = DaysInHebMonths(ylen);
  239.    m = 0;
  240.    while((jul > monlen[m]) || !monlen[m]) {
  241.       jul -= monlen[m];
  242.       m++;
  243.    }
  244.  
  245.    *hy = y;
  246.    *hm = m;
  247.    *hd = jul+1;
  248. }
  249.  
  250. /***************************************************************/
  251. /*                                                             */
  252. /*  HebNameToNum                                               */
  253. /*                                                             */
  254. /*  Convert a Hebrew month's name to its number, given the     */
  255. /*  year.                                                      */
  256. /*                                                             */
  257. /***************************************************************/
  258. #ifdef HAVE_PROTOS
  259. PUBLIC int HebNameToNum(const char *mname)
  260. #else
  261. int HebNameToNum(mname)
  262. char *mname;
  263. #endif
  264. {
  265.    int i;
  266.    int m=-1;
  267.  
  268.    for (i=0; i<14; i++)
  269.       if (!StrCmpi(mname, HebMonthNames[i])) {
  270.          m = i;
  271.      break;
  272.       }
  273.  
  274.    return m;
  275. }   
  276.  
  277. /***************************************************************/
  278. /*                                                             */
  279. /*  HebMonthname                                               */
  280. /*                                                             */
  281. /*  Convert a Hebrew month's number to its name, given the     */
  282. /*  year.                                                      */
  283. /*                                                             */
  284. /***************************************************************/
  285. #ifdef HAVE_PROTOS
  286. PUBLIC char *HebMonthName(int m, int y)
  287. #else
  288. char *HebMonthName(m, y)
  289. int m, y;
  290. #endif
  291. {
  292.    if (m != ADARA && m != ADARB) return HebMonthNames[m];
  293.  
  294.    if (!HebIsLeap[(y-1)%19]) return HebMonthNames[ADAR];
  295.    else return HebMonthNames[m];
  296. }
  297.  
  298. /***************************************************************/
  299. /*                                                             */
  300. /* GetValidHebDate                                             */
  301. /*                                                             */
  302. /* Given the day of a month, a Hebrew month number, and a      */
  303. /* year, return a valid year number, month number, and day     */
  304. /* number.  Returns 0 for success, non-0 for failure.          */
  305. /* If *dout is set to -1, then date is completely invalid.     */
  306. /* Otherwise, date is only invalid in specified year.          */
  307. /*                                                             */
  308. /* Algorithm:                                                  */
  309. /* - Convert references to Adar to Adar B.                     */
  310. /* If jahr == 0 then                                           */ 
  311. /*     - If no such date in current Hebrew year, return        */
  312. /*       failure.                                              */
  313. /* else follow jahrzeit rules:                                 */
  314. /*     - If jahr == 1: Convert 30 Kislev to 1 Tevet and        */
  315. /*                     30 Heshvan to 1 Kislev if chaser.       */
  316. /*                     Convert 30 Adar A to 1 Nisan in nonleap */
  317. /*                     This rule is NOT appropriate for a      */
  318. /*                     jahrzeit on 30 Adar A.  Use rule 2 for  */
  319. /*                     that.  However, I believe it is correct */
  320. /*                     for smachot.                            */
  321. /*     - If jahr == 2: Convert 30 Kislev to 29 Kislev and      */
  322. /*                     30 Heshvan to 29 Heshvan if chaser.     */
  323. /*                     Change 30 Adar A to 30 Shvat in nonleap */
  324. /*                                                             */
  325. /***************************************************************/
  326. #ifdef HAVE_PROTOS
  327. PUBLIC int GetValidHebDate(int yin, int min, int din,
  328.                            int *mout, int *dout, int jahr)
  329. #else
  330. int GetValidHebDate(yin, min, din, mout, dout, jahr)
  331. int yin, min, din, *mout, *dout, jahr;
  332. #endif
  333. {
  334.    char *monlen;
  335.    int ylen;
  336.  
  337.    *mout = min;
  338.    *dout = din;
  339.  
  340.    /* Do some error checking */
  341.    if (din < 1 || din > MaxMonLen[min] || min < 0 || min > 13) {
  342.       *dout = -1;
  343.       Eprint("Invalid Hebrew date");
  344.       return E_BAD_DATE;
  345.    }
  346.  
  347.    /* Convert ADAR to ADAR-B */
  348.    if (min == ADAR) *mout = min = ADARB;
  349.  
  350.    ylen = DaysInHebYear(yin);
  351.    monlen = DaysInHebMonths(ylen);
  352.  
  353.    if (din <= monlen[min]) return OK;
  354.  
  355.    switch(jahr) {
  356.       case JAHR_NONE: return E_BAD_DATE;
  357.  
  358.       case JAHR_FORWARD:
  359.          if (min == KISLEV) {
  360.             *mout = TEVET;
  361.             *dout = 1;
  362.             return OK;
  363.          } else if (min == HESHVAN) {
  364.             *mout = KISLEV;
  365.             *dout = 1;
  366.             return OK;
  367.      } else if (min == ADARA) {
  368.             if (din > 29) {
  369.            *dout = 1;
  370.            *mout = NISAN;
  371.             } else {
  372.            *dout = din;
  373.            *mout = ADARB;
  374.             }
  375.         return OK;
  376.      }
  377.  
  378.          Eprint("GetValidHebDate: (1) software error! %d", jahr);
  379.          return E_SWERR;
  380.  
  381.       case JAHR_BACKWARD:
  382.          if (min == KISLEV) {
  383.             *mout = KISLEV;
  384.             *dout = 29;
  385.             return OK;
  386.          } else if (min == HESHVAN) {
  387.             *mout = HESHVAN;
  388.             *dout = 29;
  389.             return OK;
  390.          } else if (min == ADARA) {
  391.         if (din > 29) {
  392.            *dout = 30;
  393.            *mout = SHVAT;
  394.             } else {
  395.                *mout = ADARB;
  396.            *dout = din;
  397.             }
  398.         return OK;
  399.          }
  400.  
  401.          Eprint("GetValidHebDate: (2) software error! %d", jahr);
  402.          return E_SWERR;
  403.  
  404.       default:
  405.          Eprint("GetValidHebDate: (3) software error! %d", jahr);
  406.          return E_SWERR;
  407.    }
  408. }
  409.  
  410.  
  411. /***************************************************************/
  412. /*                                                             */
  413. /*  GetNextHebrewDate                                          */
  414. /*                                                             */
  415. /*  Get the next Hebrew date on or after specified date.       */
  416. /*                                                             */
  417. /*  Returns 0 for success, non-zero for failure.               */
  418. /*                                                             */
  419. /***************************************************************/
  420. #ifdef HAVE_PROTOS
  421. PUBLIC int GetNextHebrewDate(int julstart, int hm, int hd, int jahr, int *ans)
  422. #else
  423. int GetNextHebrewDate(julstart, hm, hd, jahr, ans)
  424. int julstart, hm, hd, jahr, *ans;
  425. #endif
  426. {
  427.    int r, yout, mout, dout, jul;
  428.  
  429.    JulToHeb(julstart, &yout, &mout, &dout);
  430.  
  431.    r = 1;
  432.    while(r) {
  433.       r = GetValidHebDate(yout, hm, hd, &mout, &dout, jahr);
  434.       if (dout == -1) return r;
  435.       if (r) {
  436.          yout++;
  437.      continue;
  438.       }
  439.       jul = HebToJul(yout, mout, dout);
  440.       if (jul >= julstart) break;
  441.       else {
  442.          yout++;
  443.          r=1;  /* Force loop to continue */
  444.       }
  445.    }
  446.    *ans = jul;
  447.    return OK;
  448. }
  449.  
  450. /***************************************************************/
  451. /*                                                             */
  452. /*  ComputeJahr                                                */
  453. /*                                                             */
  454. /*  Given a date of death, compute the value to use for jahr.  */
  455. /*                                                             */
  456. /***************************************************************/
  457. #ifdef HAVE_PROTOS
  458. PUBLIC int ComputeJahr(int y, int m, int d, int *ans)
  459. #else
  460. int ComputeJahr(y, m, d, ans)
  461. int y, m, d, *ans;
  462. #endif
  463. {
  464.    char *monlen;
  465.    int len;
  466.  
  467.    *ans = JAHR_NONE;
  468.  
  469.    len = DaysInHebYear(y);
  470.    monlen = DaysInHebMonths(len);
  471.  
  472. /* Check for Adar A */
  473.    if (m == ADARA && monlen[m] == 0) {
  474.       Eprint("No Adar A in %d", y);
  475.       return E_BAD_DATE;
  476.    }
  477.  
  478.  
  479.    if (d < 1 || d > MaxMonLen[m] || m < 0 || m > 13) {
  480.       Eprint("Invalid Hebrew date");
  481.       return E_BAD_DATE;
  482.    }
  483.  
  484.    if (d > monlen[m]) {
  485.       Eprint("No %d %s %d", d, HebMonthNames[m], y);
  486.       return E_BAD_DATE;
  487.    }
  488.  
  489. /* If the jahrzeit was in Adar A, we always use JAHR_BACKWARD */
  490.    if (m == ADARA) {
  491.       *ans = JAHR_BACKWARD;
  492.       return OK;
  493.    }
  494.  
  495. /* Get lengths of months in year following jahrzeit */
  496.    len = DaysInHebYear(y+1);
  497.    monlen = DaysInHebMonths(len);
  498.  
  499.    if (d > monlen[m]) *ans = JAHR_FORWARD;
  500.    else               *ans = JAHR_BACKWARD;
  501.  
  502.    return OK;
  503. }
  504.