home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: bit.listserv.c370-l
- From: aroby@robocop.demon.co.uk (Anthony Roby)
- Path: sparky!uunet!pipex!demon!robocop.demon.co.uk!aroby
- Subject: Re: date routine
- References: <921229.171626.CST.SY06@UNTVM1>
- Organization: From Home
- Date: Wed, 30 Dec 1992 13:02:00 +0000
- Message-ID: <1992Dec30.125138.1@robocop.demon.co.uk>
- Sender: usenet@demon.co.uk
- Lines: 242
-
- David Young asked :
-
- > Does anyone know of a routine or method for calculating the difference
- > between two given days? Thanks in advance.
-
-
- Here is a description and set of algorithms I have used in the past :
-
- NOTE: This document is based on a software notebook which appeared some
- time ago in "Computing". It was entitled "Computing the Calendar" by
- Martin Vlietstra.
-
- At some stage in their careers, most programmers use various features
- associated with the calendar. Many operating systems have a number of
- system calls which can do such manipulations, but they are not always
- suitable or portable. In such circumstances, programmers must write
- their own procedures. This notebook describes some algorithms that have
- been successfully used.
-
- Ancient man based his calendar on two phenomena, the phase of the moon
- and phase of the sun. Unfortunately, one phase does not divide into the
- other exactly, so compromises had to be made. The Ancient Romans used a
- year of 365 days and a complex system of months. By the time of Julius
- Caesar, this calendar, which used the founding of Rome in 753BC as its
- baseline, was badly out of phase with the seasons. Caesar, on the
- advice of his astronomer Sosigenes, ordered that the year 46BC have 445
- days to bring the calendar back into phase with the seasons. He also
- ordered that an extra day should be added to every fourth year, thereby
- creating leap years, and he formalised the use of 12 months.
-
- The months that he defined are virtually those we know today. This was
- known as the Julian Calendar. In 816AD, the Council of Chelsea fixed
- the Christian Calendar by adopting the Julian Calendar, but using the
- birth of Christ as a baseline for numbering the years. Historians now
- believe that there was a four-year error in the new baseline.
-
- By the 16th centrury a discrepancy of 10 days had crept into the
- calendar, so Pope Gregory decreed that 10 days should be skipped during
- October 1582 and that in future any year ending in 00 should not be a
- leap year unless it was divisible by 400. Protestant England, being
- suspicious of anything coming from Rome, only adopted the Gregorian
- Calendar in 1752. The years 1800, 1900, 2100, 2200 are not leap years,
- whereas 1600, 2000 and 2400 are. Thus people who assume that any year
- which is divisible by four is a leap year will only be in error when
- dealing with dates 111 years hence or 89 years ago.
-
- The Julian day which is unconnected with the Julian year was introduced
- by Joseph Scaliger in 1582. Named in honour of Scaliger's father, it is
- a count of days since 1 January 4713BC. It is widely used by astronomers
- to record various happenings. The Julian day starts at noon, rather than
- at midnight, so is cumbersome to use.
-
- The International Standards Organisation has taken over the role of
- Caesar, the Pope and Parliament in standardising the calendar, among
- other things. Many ISO standards have equivalent British Standards
- Institute Numbers. Among the standards adopted by ISO, the following
- are used in this paper.
-
- ISO 2711: The standard week starts on a Monday and ends on a Sunday. If
- numbers are to be associated with the days of the week, then Monday is
- deemed to be a day one and Sunday day seven.
-
- ISO 2015: Week one of the year contains the first Thursday of the year.
- Thus it always contains at least four days of the year with which it is
- associated. A few days from one year are usually deemed to be part of
- the last week of the proceeding year or part of the first week of the
- succeeding year. For example, if 31 December falls on a Monday, it is
- deemed to be day one of week one of the new year.
-
- In the algorithms that follow, all variables are positive integers; all
- arrays are indexed from 1 (not from 0); comments are denoted in the
- standard C style thus: /* This is a comment */. The syntax of the
- algorithms has also been converted to C from the original article's
- pseudo code format.
-
- If a computer program is required to manipulate dates, then the dates
- should have both an internal format and an external format and a pair of
- routines to convert from one to the other. The external format is the
- one with which the program interacts with the outside world. In
- practice, it would be a set of three integers representing the day,
- month and year.
-
- The internal format is chosen to simplify manipulation of the date. The
- one used in this paper is the day_number - a count of days since a
- particular base date. Such a format is compact and very useful when
- calculating the number of days between two events - it is the difference
- between the respective day_numbers.
-
- This representation allows reasonable scope for specifying dates. Even
- if 16-bit integers are used to hold the day-number and these are
- restricted to the range -32768 to +32767. A range of +/-88 years can
- still be defined.
-
- For the period 1 March 1900 to 28 February 2100, the relationship
- between the date and the day of the week calendar follows a 28-year
- cycle, because there are seven days of the week and every fourth year is
- a leap year.
-
- The algorithms require the base date to be a Monday and 1 January of any
- year following a leap year. Suitable base dates include 1 January 1917,
- 1945, 1973 and 2001. As the algorithm describes below only works for
- positive numbers, users should exercise care in their choice of base
- date. The base date is deemed to be day one.
-
- The algorithms for conversion between date and day of the week use two
- arrays M0 and M1, each with 12 elements. These contain the number of
- days before the first day of each month for non-leap years and leap
- years respectively. The elements of the arrays correspond to January,
- February and so on.
-
- int M0[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}
-
- int M1[] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
-
- y, m and d are the calendar year, month and day, dd it a temporary
- parameter.
-
- The following hard-coded constants are used:
-
- 1461 = 365 * 4 + 1 -- the number of days in 4 years
- 1095 = 365 * 3 -- the number of days in 3 years
-
- The algorithm for computing day_number, given y, m and d is:
-
- if(y % 4 == 0)
- day_number = ((y - base_year/4) * 1461 + 1095 + M1[m] + d;
- else
- day_number = ((y - base_year/4)*1461+ ((y-1) % 4)* 365+ M0[m] + d;
-
- The algorithm for computing y, m and d, given day_number is:
-
- if((day_number % 1461) == 0) /* The last day of a leap year */
- {
- y = base_year + (day_number/1461)*4 - 1;
- m = 12;
- d = 31;
- }
- else
- {
- dd = day_number % 1461;
- y = base_year+ (day_number/1461)*4 + (dd-1)/365;
- dd = ((dd-1) % 365) + 1;
- m = 12;
-
- if((y % 4) == 0) /* a leap year */
- while(dd <= M1[m])
- m = dd - M1[m];
- else
- {
- while(dd < Mo[m])
- m = m - 1;
- d : = dd-Mo(m);
- }
- }
-
- Numeric values associated with the day of the week and with the week
- number are defined by ISO Standards 2015 and 2711. The day of the week
- is easily calculated, given the day number as described above. It is
-
- day_of_week = ((day_number-1) % 7) + 1
-
- The week number is a little more complex. Since the last few days of the
- old year and the first few days of the new year do not necessarily fall
- in a week assocaiated with that year, the algorithm for computing the
- week number does not use the calendar year. Instead it uses the 20 year
- cycle of the calendar and makes reference to a 28-element array. Each
- element of the array contains the number of weeks in each 'year' (either
- 52 or 53) of the cycle. The elements have the following values:
-
- int W[] = {52, 52, 52, 53, 52, 52, 52
- 52, 53, 52, 52, 52, 52, 52
- 53, 52, 52, 52, 52, 53, 52
- 52, 52, 52, 52, 53, 52, 52}
-
- The output variables, yr and wk are the year and week number. The other
- variables are the same as used earlier. The following hard-coded constant
- is used:
-
- 10227 = (365*4+1)*7 /* the number of days in 28 years */
-
- The agorithm is:
-
- dd = day _count % 10227;
- if(dd == 0) /* the last day of a 28 year cycle */
- {
- yr = 0;
- wk = 52;
- }
- else
- {
- yr = 1;
- wk = ((dd-1) / 7) + 1;
- while(wk > W[yr])
- {
- wk = wk - W[yr];
- yr = yr+1;
- }
- }
- yr = yr + base_year + (day_count/10227)*28 - 1;
-
- Easter falls on the Sunday following the first full moon occurring on or
- after 21 March. As far as english law is concerned, the exact means of
- caluclating Easter was laid down by an Act of Parliament in 1784, but
- this act merely confirms the decision taken at the Council of Nicaea in
- 325AD. The algorithm that follows makes use of the fact that every 19
- years the Paschal new moon falls on the same day.
-
- The Golden Year as defined by the Council of Nicaea is represented in
- the algorithm by G. The number of day from 21 March until the Paschal
- new year for each year in the 19-year cycle is given by the array
- 'offset'. It contains the following values:
-
- int offset[] = {24, 13, 2, 21, 10, 28, 18, 7, 26, 15,
- 4, 23, 12, 1, 20, 9, 27, 17, 6}
-
- Day_no is a function which returns the day_number of year, month and day
- specified. The algorithm of finding the day_number associated which
- Easter for the year y is
-
- G = 1 + (y % 19):
- Easter = ((offset[G]+ day_no(y , 3, 21)) / 7 + 1) * 7
-
- Most of the festivals in the Christian Calendar use either Christmas or
- Easter as their baseline. Some of these festivals, such as Shrove
- Tuesday, Pancake day, have traditinal celebrations, while others, such as
- Whit Monday are bank holidays in many EC countries. These days include:
-
- Shrove Tuesday = Easter - 40
- Good Friday = Easter - 2
- Easter Monday = Easter + 1
- Ascension Day = Easter + 39
- Whit Monday = Easter + 50
-
- These festivals can easily be calculated by adding or substracting the
- appropriate number from the day_number representing Easter and then
- converting back to the user's (year, month, day) format.
-
-
- Anthony Roby aroby@robocop.demon.co.uk
- 7 Delcombe Avenue, aroby@cix.compulink.co.uk
- Worcester Park,
- Surrey. ENGLAND. Voice : +44 81 337 0913
-