Gregorian day numbers
By , journalist and programmer
In case calendar dates are a substantial part of a computer application, the programmer who creates it can hardly omit a well working day numbering system. After all, calendar dates in themselves are not ready-made to do easy math on. Only when dates are converted to numbers (and those numbers are in sequence!) calculations on dates become rather easy. The only question is: which of the daynumbering algorithms fits best?
I'm proud to announce that since today you've one additional choice in this respect, the Gregorian Day Number. As its name points out, the Gregorian Day Number algorithm fits in the Gregorian Calendar which we use today. Before I come to explain it, I like to discuss a few existing systems in order to see their possible drawbacks.

Julian Day Number
One of the existing systems, and probably the most popular one, is the Julian Day Number, a tool often used by astronomers. It counts days from November 25, 4714 BC on the proleptic Gregorian Calendar, which is January 1, 4713 BC on the Julian Calendar. The drawbacks of this system are obvious:
   it creates very huge numbers for calendar dates in our era. For example, the number for January 1, 2002 is: 2,452,276.
   day numbers in the approx. 60(!) centuries before October 15, 1582 are based upon dates in a calendar that did not exist.

Native Windows' daycount
Another method is to use the 'native' way, 32-bit Windows keeps track of time. This timetrack is stored in portions of 100 nanoseconds (a 10 millionth of a second) from January 1, 1601 [at 00:00:00 UTC], being the first day of the first full century in the Gregorian Calendar. If you divide the huge value, found for a certain date, by the number of portions of 100 nanoseconds in a 24-hours day, you get an integer value, starting at 0 for January 1, 1601, 1 for January 2, 1601 etc. Again the drawbacks are evident:
   the calculations involved are very huge (an eight-byte quad-integer is necessary to work with these numbers).
   the system skips approx. the first nineteen years (10/15/1582 - 12/31/1600) of the Gregorian Calendar.
   programmers under DOS, for instance the many users of Power Basic for DOS, have no access to this system.

A few lines of history
It is not my intention to write a deepdigging article on calendrical history. After all, this is a place for programmers, not for historians. But a few lines are unavoidable though. The Gregorian Calendar was proclaimed in 1582 by the Papal Encyclical letter Inter Gravissimas from Pope Gregory XIII (hence the calendar's name). It replaced the Julian Calendar that had become out of synchronization, due to a wrong algorithm for Leap Years.

In the Julian Calendar every fourth year was a leap year. On behalf of Pope Gregory's astronomers this rule was changed as follows: every fourth year is a leap year except years that can be evenly divided by 100 (the so called century years), unless they can (also) be divided by 400. Thus, 1600 was a leap year and so was recent 2000, but 1700, 1800 and 1900 were not, nor will 2100, 2200 and 2300 be. You may also put it this way: a period of 4 centuries does not count 100 leap years, but 97. To bring the calendar in line with the new leap year rule, 10 days were 'eliminated': October 15, the first day of the Gregorian Calendar, followed October 4 in the abandoned Julian Calendar. In fact those 10 days were approximately no more and no less than the false leap days that had 'polluted' the calendar in past century years.

Because the Gregorian Calendar was proclaimed by 'a roman catholic pope', it was not immediately implemented everywhere. Authorities in protestant regions were sometimes reluctant for ages and so were Russian Orthodox prelates. In Russia the new calendar was accepted in the 20th century, after the communist party took power in what was then called The Soviet Union. The old Julian Calendar was 13 days out of sync by that time, so the soviets had to skip 13 days. That explains why the Russians always commemorate their October Revolution in November.

The formula
As said, October 15, 1582 is the first day of the modern calendar. Hence it is also the starting point in the algorithm I'm going to present here. In other words: that date is Gregorian Day Number 1. In some publications this day number is referred to as Lilian Day Number 1, but do'nt be mistaken. A Lilian Day Number is just a Julian Day Number from which 2,299,160 days were substracted. So the 60 centuries of more or less useless daynumbers are not ruled out, only hidden. My Gregorian Day Number formula does not utilize such a trick. It has its own, genuine mathematics.

In order to convert a date in the Gregorian Calendar to a Gregorian Day Number, a few simple rules apply:
   months, except February, have an average length of 30.6 days.
   February should be moved to the tail of the month-sequence (so, Jan. = 13th, Feb. = 14th month of previous year).
   initially we count one leap day in 4 years (365.25 days per year), but this should be corrected for century years.

This is the Gregorian FUNCTION (parameters: year, month and day) in human language. First thing to do is to check the month. If it is January or February [in code: IF month < 3] we add 12 to make January the 13th or February the 14th month. This not only moves February to the tail of the month sequence but also makes a leap day the last day of the year. Beware: both, January and February, are considered to be months of the previous year, so we have to substract one from the year variable as well.
Of course we don't take the year of date in account, it may not be fully elapsed yet (substract 1 year). And, also, we exclude years before 1600 (substract 1599 years). This is how these two substractions can be combined in one line of code: year = year - 1600.
So far our preparations. Now we can start counting elapsed days:

  • 6286 days from 10/15/1582 to 12/31/1599
  • add 365.25 times year days (INT rounded)
  • substract century leap days: year \ 100 days
  • re-add the valid ones: year \ 400 days
  • add days in elapsed months: 30.6 times (month - 1) + 0.2 days (exclude month of date)
        [0.2 is inserted here as a rounding factor, necessary to keep day numbers in sequence]
  • finally add days in month of date: day days
The other way
To do it the other way, i.e. to convert a number back to the date it represents, you should consider:
   years begin on March 1
   in calculations over centuries you should use the number of days in 400 years (= 146097 days)
   in calculations within 1 century you should use the number of days in 4 years (= 1461 days)

The formula is contained in a procedure (SUB) of which GDN - Gregorian Day Number - is the input parameter, while year, month and day are output parameters. For the time being you should construct the date format, such as "mm-dd-yyyy", from these values yourself. Almost all the calculations in the SUB are based upon the elimination of fully elapsed days. As said, our years should begin on March 1. To achieve this, we need to alter the base of the day number passed. Its original base is 10/15/1582 = 1. If we add 614565 days, its base will become: 03/01/-0100 = 1 (614565 is the number of days between March 1, -100 and October 15, 1582 AD). A predictable question is: why extend our computation that far backward? Don't ask me, I don't know. Starting in years closer to 1582, which I certainly would prefer, did not work correctly during the many hours I've spent to test this algorithm.

Once we have altered the base of our computations, we can calculate the number of centuries that have fully elapsed since March 1, -100. Four centuries count, as said earlier, exactly 146097 days (400 times 365 plus 97 leap days). So, if we multiply our modified input by 4 and divide the result by 146097, we get the number of elapsed centuries. We also can find the number of days in those centuries: 146097 times centuries divided by 4 (CEIL rounded). Substract those days from the initial value (i.e. input + 614565) and we have eliminated the days in the fully elapsed centuries. Our pseudo code so far:
GDN = GDN + 614565
centuries = (4 * GDN) \ 146097
GDN = GDN - CEIL((146097 * centuries) / 4)

What remains after eliminating the days in elapsed centuries is undoubtedly the number of days in the century of date. The next step is to get the number of elapsed years within that century. Piece of cake: multiply the remaining days 4 times and divide the result by 1461 days. Once done, we can get the number of days in those years and eliminate them as well. In code:
years = (4 * GDN + 3) \ 1461
    ' NOTE: 3 days inserted as rounding factor
GDN = GDN - ((1461 * years) \ 4) + 31

In the previous step we have seen that we did'nt eliminate all the days in elapsed years. We kept 31 'in stock'. These extra 31 days have to do with the odd month sequence (March = month 1) we are following here. In the meantime we know how many centuries have passed by since March 1, -100 and how many years there are in the current century (0 - 99). Using those data, we can construct the 4-digit year. By using '(centuries - 1)' the 100 years before zero are skipped correctly.
year = (centuries - 1) * 100 + years

The final step is to get the month and eliminate the days in fully elapsed months. What remains then is the number of days in the month of date (= day in month). The formula used here looks a bit strange, but 2447 days divided by 80 gives an average of approximately 30.6 days per month:
month = 80 * GDN \ 2447
day = GDN - (2447 * month \ 80)

Is this really the end? Not quite, because we have to restore the month sequence back to normal, so we add 2. This sets March (was 1) to 3 .... December to 12, but, unfortunately, also: January to 13 and February to 14. In order to correct this we simply use the routine the Gregorian FUNCTION started with (only in the opposite way):
IF month > 12 THEN
  month = month - 12
  INCR year
END IF

More to come
Of course, the functions mentioned above are not very valuable in themselves. As a programmer's help utility, however, they can become very useful since they make it rather easy to manipulate calendar dates within any kind of application. In a next article I'll unveil a bunch of calendar-related programming tasks based upon these routines. In other words: "to be continued"


Thanks
I want to express my gratitude to Claus Tøndering from Denmark who is a widely known expert in this field. Claus was so kind to reply to quite a number of questions, I asked him. You definitely should read his Calendar FAQ if you want to know more about calendar science.


Where to download?
   Click here to download the source code.
   Back to homepage

Copyright © 2002: Egbert Zijlema
Failed to execute script '/cgi-bin/hidden.exe?gregorian': Win32 Error Code = 2