home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 2 / ctrom_ii_b.zip / ctrom_ii_b / PROGRAM / C / JULCAL10 / JULCAL.C < prev    next >
C/C++ Source or Header  |  1992-12-21  |  9KB  |  299 lines

  1. /* 
  2.     Julian (sense 1) date routines, handling both Julian (sense 2) and
  3.     Gregorian calendars
  4.  
  5.     SYNOPSIS
  6.         long juldn (struct tm *bdt)
  7.         long juldnj (struct tm *bdt, long Transition)
  8.         long juldnd (struct tm *bdt, struct tm *Transition_date)
  9.  
  10.         struct tm *julcd (long J)
  11.         struct tm *julcdj (long J, long Transition)
  12.         struct tm *julcdd (long J, struct tm *Transition_date)
  13.  
  14.         extern long jul_transition;
  15.  
  16.     DESCRIPTION
  17.  
  18.     juldn* returns the Julian day number (days since Jan 1, 4713 B.C.)
  19.     for the date specified by the struct tm (a broken down time)
  20.     pointed to by 'bdt'.  Only the tm_year, tm_mon, and tm_mday fields
  21.     are examined.  If the month or day fields are out of their normal
  22.     range, they are adjusted.  The tm_wday and tm_yday fields are also
  23.     set.
  24.  
  25.     julcd* returns a pointer to a struct tm filled in with the date
  26.     corresponding to the Julian day number 'J'.  Five fields are set:
  27.     tm_year, tm_mon, tm_mday, tm_wday, and tm_yday.  The pointer is to
  28.     a static structure which is reused on each call.
  29.  
  30.     For both juldn and julcd, the Gregorian calendar is assumed
  31.     provided the Julian day number falls on or after the value in the
  32.     global variable 'jul_transition', otherwise the Julian calendar is
  33.     used.  'jul_transition' is initialized to 2361222 (or September 14,
  34.     1752), as in the United Kingdom and the colonies.  A different
  35.     transition can be specified by Julian day number (for juldnj or
  36.     julcdj) or Gregorian date (for juldnd or julcdd).  Alternatively,
  37.     'jul_transition' can be reset.  If the transition is specified by
  38.     date, ensure it is interpreted as a Gregorian date by using julcdj
  39.     with a small number:
  40.  
  41.         jul_transition = julcdj(&my_transition_date, 1L);
  42.  
  43.     Algorithm is valid from 4713 B.C. to 19,999 A.D.  For caveats,
  44.     see below.
  45.  
  46.     Aside: In a struct tm, the tm_year field is "year-1900".  For the
  47.     year 1 A.D., that would be -1899.  There was no year 0 (the number
  48.     zero had not been invented!), so the previous year was 1 B.C.,
  49.     which is represented by -1900.
  50.  
  51.     HISTORY
  52.  
  53.     $Id: julcal.c%v 1.10 1992/12/13 03:15:59 jrv Exp jrv $
  54.  
  55.     $Log: julcal.c%v $
  56.  * Revision 1.10  1992/12/13  03:15:59  jrv
  57.  * default transition date is that for the United Kingdom
  58.  *
  59.  * Revision 1.9  1992/12/13  03:07:07  jrv
  60.  * juldnj gives correct value even if month is outside normal range.
  61.  * juldnj normalizes mday, month, and year.
  62.  * _juldnj introduced to let julcdj set yday without causing infinite recursion.
  63.  *
  64.  * Revision 1.8  1992/12/08  00:23:38  jrv
  65.  * Test program moved to a separate file.
  66.  *
  67.  * Revision 1.7  1992/12/06  01:57:20  jrv
  68.  * julcd* return pointers to a static struct tm, like localtime and gmtime.
  69.  *
  70.  * Revision 1.6  1992/12/05  23:14:58  jrv
  71.  * The default transition date is a global variable, initialized to 15 Oct 1582.
  72.  * Some variable names changed.  All variables are lower case.
  73.  *
  74.  * Revision 1.5  1992/12/05  22:20:11  jrv
  75.  * juldnd accepts a struct tm (a "broken-down time") instead of a 
  76.  * unique struct.  julcdd produces a struct tm (including tm_wday 
  77.  * and tm_yday fields) instead of a unique struct.
  78.  *
  79.  * Revision 1.4  1992/12/05  20:04:51  jrv
  80.  * Test program prints input values, then computed values.
  81.  * Test program is silent by default.
  82.  *
  83.  * Revision 1.3  1992/12/04  02:57:54  jrv
  84.  * Reformatted to more standard C.
  85.  * Eliminated some redundant typedefs.
  86.  * Type Year is now an int rather than a long.
  87.  * Some variables converted to lower case.
  88.  *
  89.  * Revision 1.2  1992/12/04  02:15:58  jrv
  90.  * A Year is no longer unsigned, so years BC work.
  91.  * Regression test driver added.
  92.  *
  93.  * Revision 1.1  1992/12/04  00:21:37  jrv
  94.  * Initial revision
  95.  *
  96.  
  97.     Translated from Pascal to C by Jim Van Zandt, July 1992.
  98.  
  99.     Error-free translation based on error-free PL/I source
  100.  
  101.     Based on Pascal code copyright 1985 by Michael A. Covington,
  102.     published in P.C. Tech Journal, December 1985, based on formulae
  103.     appearing in Astronomical Formulae for Calculators by Jean Meeus
  104.  
  105.     Reconversion to normal Julian epoch, integer arithmetic and
  106.     4000-year correction by John W. Kennedy
  107.  
  108.     [The 4000-year adjustment is controversial.  It is not mentioned in
  109.     the paper "Calendrical Calculations" by Nachum Dershowitz and
  110.     Edward M.  Reingold in Software Practice and Experience, v 20 n 9
  111.     pp 899-928 (Sep 1990).  I have left it in mainly because it will
  112.     make no difference for a very long time.  - jvz]
  113.  
  114.     Historical exceptions _not_ allowed for in this package:
  115.  
  116.     Until Julius Caesar established the Julian calendar in 45 B.C.,
  117.     calendars were irregular.  This package assumes the Julian calendar
  118.     back to 4713 B.C.
  119.  
  120.     The Julian calendar was altered in 8 B.C.  From 45 B.C. to 8 B.C.,
  121.     the months were
  122.        Jan=31, Feb=29(30), Mar=31, Apr=30, May=31, Jun=30,
  123.        Jul=31, Aug=30,     Sep=31, Oct=30, Nov=31, Dec=30
  124.     This package assumes the month lengths as we know them.
  125.  
  126.     Leap years from 45 B.C.  to 8 A.D.  were miscalculated: (45, 42,
  127.     39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, then none at all until 8
  128.     A.D.) This package assumes leap years every four years, as they
  129.     were meant to have been.
  130.  
  131.     January 1 was not always the first day of the year.  The United
  132.     Kingdom, in particular, started the year on March 25 until 1752. 
  133.     (However, the year ended on December 31, leaving the days between
  134.     in limbo.) This package assumes January 1 is the first day of the
  135.     year.
  136.  
  137.     Leap-year day was originally done by having February 24 (25 from 45
  138.     to 8 B.C.) twice.  This package assumes Leap-year day is February
  139.     29.
  140.  
  141.     "Transition" argument is the first Julian date to be considered as
  142.     belonging to the Gregorian calendar.  Usual values are:
  143.  
  144.     2299161 = October 5/15, 1582,       as in Rome, or
  145.     2361222 = September 3/14, 1752,     as in the United Kingdom 
  146.                                         and the Colonies 
  147.  
  148.     For more on the history of calendars, including transition dates in
  149.     other countries, see Bob Douglas' article in dates.txt.
  150.  
  151. */
  152.  
  153. #include <time.h>
  154. #include "julcal.h"
  155.  
  156. typedef long Julian;
  157. typedef int Year;
  158. typedef int Month;
  159. typedef int Day;
  160.  
  161. Julian jul_transition=JUL_ENGLAND;    
  162.  
  163. /*
  164. Julian juldnj (struct tm *bdt, Julian Transition);
  165. Julian juldn (struct tm *bdt);
  166. Julian juldnd (struct tm *bdt, struct tm *Transition_date);
  167.  
  168. struct tm *julcdj (Julian J, Julian Transition);
  169. struct tm *julcd (Julian J);
  170. struct tm *julcdd (Julian J, struct tm *Transition_date);
  171. */
  172.  
  173. typedef long Work;
  174.  
  175. static Julian 
  176. _juldnj(bdt, Transition) struct tm *bdt; Julian Transition;
  177. {
  178.     Year ay, y, dy;
  179.     Month m;
  180.     Julian jd_julian, jd_gregorian, jd;
  181.  
  182.     y = bdt->tm_year + 1900;
  183.     m = bdt->tm_mon;
  184.  
  185.     dy = m/12;
  186.     y += dy;
  187.     m -= dy*12;
  188.             /* assert( -11 <= m && m <= 11 ) */
  189.     if(m < 0)
  190.         {
  191.         m += 12;
  192.         y--;
  193.         }
  194.             /* assert( 0 <= m && m <= 11 ) */
  195.  
  196.     if ( m < 2 ) 
  197.         {
  198.         m += 1 + 12;
  199.         y--;
  200.         }
  201.     else
  202.         m++;
  203.  
  204.     ay = y + 4716;
  205.     jd_julian = ((1461*(Work)ay) >> 2) + (153*(m + 1)/5)
  206.            + (Work)bdt->tm_mday - 1524;
  207.     jd_gregorian = jd_julian + 2 - y/100 + y/400 - y/4000;
  208.     if ( jd_gregorian >= Transition ) jd = jd_gregorian; 
  209.     else jd = jd_julian;
  210.     return jd;
  211. }
  212.  
  213.     /* this wrapper can call julcdj without causing infinite recursion */
  214. Julian 
  215. juldnj(bdt, Transition) struct tm *bdt; Julian Transition;
  216. {    Julian jd;
  217.     struct tm *normal;
  218.  
  219.     jd = _juldnj(bdt, Transition);
  220.  
  221.     normal = julcdj(jd, Transition);
  222.     bdt->tm_year = normal->tm_year;
  223.     bdt->tm_mon = normal->tm_mon;
  224.     bdt->tm_mday = normal->tm_mday;
  225.     bdt->tm_wday = normal->tm_wday;
  226.     bdt->tm_yday = normal->tm_yday;
  227.  
  228.     return jd;
  229. }
  230.  
  231. Julian 
  232. juldn (bdt) struct tm *bdt; 
  233. {    return juldnj (bdt, jul_transition);
  234. }
  235.  
  236. Julian 
  237. juldnd (bdt, Transition_date) struct tm *bdt; struct tm *Transition_date;
  238. {    return juldnj (bdt, _juldnj (Transition_date, 1L));
  239. }
  240.  
  241. struct tm *
  242. julcdj (jd, Transition) Julian jd; Julian Transition;
  243. {
  244.     Julian aa, ab, a;
  245.     Work b, d, ee;
  246.     Year ay;
  247.     Month em, m;
  248.     Year y;
  249.     struct tm newyears;
  250.     static struct tm date;
  251.  
  252.     memset(&date, 0, sizeof(date));
  253.  
  254.     if ( jd < Transition ) /* Julian Calendar */
  255.         a = (Work)(jd);
  256.     else /* Gregorian Calendar */ 
  257.         {
  258.         aa = jd - 1721120L;
  259.         ab = 31*(aa/1460969L); aa = aa % 1460969L;
  260.         ab = ab + 3*(aa/146097L); aa = aa % 146097L;
  261.         if ( aa == 146096L ) ab = ab + 3; 
  262.         else ab = ab + aa/36524L;
  263.         a = jd + (ab - 2);
  264.         }
  265.     b = a + 1524;
  266.     ay = (Year)((20*b - 2442)/7305);
  267.     d = (1461*(Work)(ay)) >> 2;
  268.     ee = b - d;
  269.     em = (Month)(10000L*ee/306001L);
  270.  
  271.     date.tm_mday = (Day)(ee - 306001L*em/10000L);
  272.  
  273.     m = em - 1;
  274.     if(m > 12) m -= 12;
  275.     date.tm_mon = m - 1;
  276.  
  277.     if ( m > 2 ) y = ay - 4716; 
  278.     else y = ay - 4715;
  279.     date.tm_year = y - 1900;
  280.  
  281.     date.tm_wday = (jd+1)%7;
  282.  
  283.     newyears = date;
  284.     newyears.tm_mon = 0;
  285.     newyears.tm_mday = 1;
  286.     date.tm_yday = jd - _juldnj(&newyears, Transition);
  287.     return &date;
  288. }
  289.  
  290. struct tm *
  291. julcd(jd) Julian jd; 
  292. {    return julcdj (jd, jul_transition);
  293. }
  294.  
  295. struct tm *
  296. julcdd(jd, Transition_date) Julian jd; struct tm *Transition_date; 
  297. {    return julcdj (jd, _juldnj (Transition_date, 1L));
  298. }
  299.