home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #31 / NN_1992_31.iso / spool / bit / listserv / c370l / 169 < prev    next >
Encoding:
Text File  |  1992-12-30  |  11.5 KB  |  254 lines

  1. Newsgroups: bit.listserv.c370-l
  2. From: aroby@robocop.demon.co.uk (Anthony Roby)
  3. Path: sparky!uunet!pipex!demon!robocop.demon.co.uk!aroby
  4. Subject: Re: date routine
  5. References: <921229.171626.CST.SY06@UNTVM1>
  6. Organization: From Home
  7. Date: Wed, 30 Dec 1992 13:02:00 +0000
  8. Message-ID: <1992Dec30.125138.1@robocop.demon.co.uk>
  9. Sender: usenet@demon.co.uk
  10. Lines: 242
  11.  
  12. David Young asked :
  13.  
  14. > Does anyone know of a routine or method for calculating the difference
  15. > between two given days?  Thanks in advance.
  16.  
  17.  
  18. Here is a description and set of algorithms I have used in the past :
  19.  
  20.       NOTE:  This document is based on a software notebook which appeared some
  21.       time ago in "Computing".  It was entitled "Computing the Calendar" by
  22.       Martin Vlietstra.
  23.  
  24.       At some stage in their careers, most programmers use various features
  25.       associated with the calendar.  Many operating systems have a number of
  26.       system calls which can do such manipulations, but they are not always
  27.       suitable or portable.  In such circumstances, programmers must write
  28.       their own procedures.  This notebook describes some algorithms that have
  29.       been successfully used.
  30.  
  31.       Ancient man based his calendar on two phenomena, the phase of the moon
  32.       and phase of the sun.  Unfortunately, one phase does not divide into the
  33.       other exactly, so compromises had to be made.  The Ancient Romans used a
  34.       year of 365 days and a complex system of months.  By the time of Julius
  35.       Caesar, this calendar, which used the founding of Rome in 753BC as its
  36.       baseline, was badly out of phase with the seasons.  Caesar, on the
  37.       advice of his astronomer Sosigenes, ordered that the year 46BC have 445
  38.       days to bring the calendar back into phase with the seasons.  He also
  39.       ordered that an extra day should be added to every fourth year, thereby
  40.       creating leap years, and he formalised the use of 12 months.
  41.  
  42.       The months that he defined are virtually those we know today.  This was
  43.       known as the Julian Calendar.  In 816AD, the Council of Chelsea fixed
  44.       the Christian Calendar by adopting the Julian Calendar, but using the
  45.       birth of Christ as a baseline for numbering the years.  Historians now
  46.       believe that there was a four-year error in the new baseline.
  47.  
  48.       By the 16th centrury a discrepancy of 10 days had crept into the
  49.       calendar, so Pope Gregory decreed that 10 days should be skipped during
  50.       October 1582 and that in future any year ending in 00 should not be a
  51.       leap year unless it was divisible by 400.  Protestant England, being
  52.       suspicious of anything coming from Rome, only adopted the Gregorian
  53.       Calendar in 1752.  The years 1800, 1900, 2100, 2200 are not leap years,
  54.       whereas 1600, 2000 and 2400 are.  Thus people who assume that any year
  55.       which is divisible by four is a leap year will only be in error when
  56.       dealing with dates 111 years hence or 89 years ago.
  57.  
  58.       The Julian day which is unconnected with the Julian year was introduced
  59.       by Joseph Scaliger in 1582.  Named in honour of Scaliger's father, it is
  60.       a count of days since 1 January 4713BC.  It is widely used by astronomers
  61.       to record various happenings.  The Julian day starts at noon, rather than
  62.       at midnight, so is cumbersome to use.
  63.  
  64.       The International Standards Organisation has taken over the role of
  65.       Caesar, the Pope and Parliament in standardising the calendar, among
  66.       other things.  Many ISO standards have equivalent British Standards
  67.       Institute Numbers.  Among the standards adopted by ISO, the following
  68.       are used in this paper.
  69.  
  70.       ISO 2711:  The standard week starts on a Monday and ends on a Sunday.  If
  71.       numbers are to be associated with the days of the week, then Monday is
  72.       deemed to be a day one and Sunday day seven.
  73.  
  74.       ISO 2015:  Week one of the year contains the first Thursday of the year.
  75.       Thus it always contains at least four days of the year with which it is
  76.       associated.  A few days from one year are usually deemed to be part of
  77.       the last week of the proceeding year or part of the first week of the
  78.       succeeding year.  For example, if 31 December falls on a Monday, it is
  79.       deemed to be day one of week one of the new year.
  80.  
  81.       In the algorithms that follow, all variables are positive integers; all
  82.       arrays are indexed from 1 (not from 0); comments are denoted in the
  83.       standard C style thus: /* This is a comment */.  The syntax of the
  84.       algorithms has also been converted to C from the original article's
  85.       pseudo code format.
  86.  
  87.       If a computer program is required to manipulate dates, then the dates
  88.       should have both an internal format and an external format and a pair of
  89.       routines to convert from one to the other.  The external format is the
  90.       one with which the program interacts with the outside world.  In
  91.       practice, it would be a set of three integers representing the day,
  92.       month and year.
  93.  
  94.       The internal format is chosen to simplify manipulation of the date.  The
  95.       one used in this paper is the day_number - a count of days since a
  96.       particular base date.  Such a format is compact and very useful when
  97.       calculating the number of days between two events - it is the difference
  98.       between the respective day_numbers.
  99.  
  100.       This representation allows reasonable scope for specifying dates.  Even
  101.       if 16-bit integers are used to hold the day-number and these are
  102.       restricted to the range -32768 to +32767.  A range of +/-88 years can
  103.       still be defined.
  104.  
  105.       For the period 1 March 1900 to 28 February 2100, the relationship
  106.       between the date and the day of the week calendar follows a 28-year
  107.       cycle, because there are seven days of the week and every fourth year is
  108.       a leap year.
  109.  
  110.       The algorithms require the base date to be a Monday and 1 January of any
  111.       year following a leap year.  Suitable base dates include 1 January 1917,
  112.       1945, 1973 and 2001.  As the algorithm describes below only works for
  113.       positive numbers, users should exercise care in their choice of base
  114.       date.  The base date is deemed to be day one.
  115.  
  116.       The algorithms for conversion between date and day of the week use two
  117.       arrays M0 and M1, each with 12 elements.  These contain the number of
  118.       days before the first day of each month for non-leap years and leap
  119.       years respectively.  The elements of the arrays correspond to January,
  120.       February and so on.
  121.  
  122.          int M0[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}
  123.  
  124.          int M1[] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
  125.  
  126.       y, m and d are the calendar year, month and day, dd it a temporary
  127.       parameter.
  128.  
  129.       The following hard-coded constants are used:
  130.  
  131.       1461 = 365 * 4 + 1   -- the number of days in 4 years
  132.       1095 = 365 * 3       -- the number of days in 3 years
  133.  
  134.       The algorithm for computing day_number, given y, m and d is:
  135.  
  136.          if(y % 4 == 0)
  137.             day_number = ((y - base_year/4) * 1461 + 1095 + M1[m] + d;
  138.          else
  139.             day_number = ((y - base_year/4)*1461+ ((y-1) % 4)* 365+ M0[m] + d;
  140.  
  141.       The algorithm for computing y, m and d, given day_number is:
  142.  
  143.          if((day_number % 1461) == 0)   /* The last day of a leap year */
  144.          {
  145.             y = base_year + (day_number/1461)*4 - 1;
  146.             m = 12;
  147.             d = 31;
  148.          }
  149.          else
  150.          {
  151.             dd = day_number % 1461;
  152.             y  = base_year+ (day_number/1461)*4 + (dd-1)/365;
  153.             dd = ((dd-1) % 365) + 1;
  154.             m  = 12;
  155.  
  156.             if((y % 4) == 0)   /* a leap year */
  157.                while(dd <= M1[m])
  158.                   m = dd - M1[m];
  159.             else
  160.             {
  161.                while(dd < Mo[m])
  162.                   m = m - 1;
  163.                d : = dd-Mo(m);
  164.             }
  165.          }
  166.  
  167.       Numeric values associated with the day of the week and with the week
  168.       number are defined by ISO Standards 2015 and 2711.  The day of the week
  169.       is easily calculated, given the day number as described above.  It is
  170.  
  171.          day_of_week = ((day_number-1) % 7) + 1
  172.  
  173.       The week number is a little more complex.  Since the last few days of the
  174.       old year and the first few days of the new year do not necessarily fall
  175.       in a week assocaiated with that year, the algorithm for computing the
  176.       week number does not use the calendar year.  Instead it uses the 20 year
  177.       cycle of the calendar and makes reference to a 28-element array.  Each
  178.       element of the array contains the number of weeks in each 'year' (either
  179.       52 or 53) of the cycle.  The elements have the following values:
  180.  
  181.          int W[] =  {52,  52,  52,  53,  52,  52,  52
  182.                      52,  53,  52,  52,  52,  52,  52
  183.                      53,  52,  52,  52,  52,  53,  52
  184.                      52,  52,  52,  52,  53,  52,  52}
  185.  
  186.       The output variables, yr and wk are the year and week number.  The other
  187.       variables are the same as used earlier. The following hard-coded constant
  188.       is used:
  189.  
  190.          10227 = (365*4+1)*7   /* the number of days in 28 years */
  191.  
  192.       The agorithm is:
  193.  
  194.          dd = day _count % 10227;
  195.          if(dd == 0)       /* the last day of a 28 year cycle  */
  196.          {
  197.             yr = 0;
  198.             wk = 52;
  199.          }
  200.          else
  201.          {
  202.             yr = 1;
  203.             wk = ((dd-1) / 7) + 1;
  204.             while(wk > W[yr])
  205.             {
  206.                wk = wk - W[yr];
  207.                yr = yr+1;
  208.             }
  209.          }
  210.          yr = yr + base_year + (day_count/10227)*28 - 1;
  211.  
  212.       Easter falls on the Sunday following the first full moon occurring on or
  213.       after 21 March.  As far as english law is concerned, the exact means of
  214.       caluclating Easter was laid down by an Act of Parliament in 1784, but
  215.       this act merely confirms the decision taken at the Council of Nicaea in
  216.       325AD.  The algorithm that follows makes use of the fact that every 19
  217.       years the Paschal new moon falls on the same day.
  218.  
  219.       The Golden Year as defined by the Council of Nicaea is represented in
  220.       the algorithm by G.  The number of day from 21 March until the Paschal
  221.       new year for each year in the 19-year cycle is given by the array
  222.       'offset'.  It contains the following values:
  223.  
  224.          int offset[] = {24, 13,  2, 21, 10, 28, 18,  7, 26, 15,
  225.                           4, 23, 12,  1, 20,  9, 27, 17,  6}
  226.  
  227.       Day_no is a function which returns the day_number of year, month and day
  228.       specified.  The algorithm of finding the day_number associated which
  229.       Easter for the year y is
  230.  
  231.          G = 1 + (y % 19):
  232.          Easter = ((offset[G]+ day_no(y , 3, 21)) / 7 + 1) * 7
  233.  
  234.       Most of the festivals in the Christian Calendar use either Christmas or
  235.       Easter as their baseline.  Some of these festivals, such as Shrove
  236.       Tuesday, Pancake day, have traditinal celebrations, while others, such as
  237.       Whit Monday are bank holidays in many EC countries.  These days include:
  238.  
  239.          Shrove Tuesday = Easter - 40
  240.          Good Friday    = Easter -  2
  241.          Easter Monday  = Easter +  1
  242.          Ascension Day  = Easter + 39
  243.          Whit Monday    = Easter + 50
  244.  
  245.       These festivals can easily be calculated by adding or substracting the
  246.       appropriate number from the day_number representing Easter and then
  247.       converting back to the user's (year, month, day) format.
  248.  
  249.  
  250.   Anthony Roby                   aroby@robocop.demon.co.uk
  251.   7 Delcombe Avenue,             aroby@cix.compulink.co.uk
  252.   Worcester Park,
  253.   Surrey.  ENGLAND.              Voice : +44 81 337 0913
  254.