David Burki is a Programmer/Analyst employed by PDA, Inc. His recent projects include application development under OS/2 Presentation Manager as well as MS-Windows and MS-DOS. You may contact him at PDA, Inc. 11600 College Blvd., Suite 100, Overland Park, KS 66210, (913) 469-8700.
Whether you are programming business applications, calculating the position of celestial objects, or just trying to figure your personal biorhythm, the need to perform arithmetic with calendar dates is crucial to computations involving time. Calendar dates are used everywhere. Interest (and penalties) are calculated, subscription renewal notices are mailed, and your physical, emotional, and intellectual biorhythmic peaks and slumps are just a few things based on dates. The problem is that there is no direct method to perform arithmetic on calendar dates. The routines presented here will allow you to determine the number of days between two dates, calculate the calendar date some number of days before or after a given date, determine the day of the week a certain date falls on, and determine whether a given year is a leap year. In addition, the internal representation of these dates provide a compact method for storing dates.
The Trouble with Dates
The problem with date computations stems from the way we represent calendar dates. The Gregorian calendar (see the sidebar, "A Brief History of the Calendar"), combines three different numbering units to create a calendar date: the number of days in a month, the number of months in a year, and the number of years since the beginning of the Christian era. Each of these "digits" are of a different base. Years are base 10, months are base 12, and days are either base 28, 29, 30 or 31. To easily do arithmetic on a calendar date, it must first be converted into a single number. Converting a calendar date into a single number yields a unique Julian Day number for that date. Notice I said Julian Day, not Julian date. A Julian Day number is a method astronomers use to identify dates. The Julian Day method was developed by Joseph Scaliger about 1577 A.D. as a means by which all days within recorded time would be assigned consecutive numbers (Harvey 1983). Contrary to popular belief, Scaliger named his method after his father, not Julius Caesar. A Julian date, on the other hand, is a calendar date based on the Julian calendar.
Conversions
At the heart of date manipulation is the ability to convert a calendar date to a Julian Day number and back. Converting a Gregorian date to a Julian Day number is accomplished by the function ToJul in Listing 1. ToJul takes the month, day, and year as parameters and returns the Julian Day number as a long integer. The constants used in the function were derived by the algorithm's originator and are critical to being able to successfully convert the Julian Day number back to a Gregorian date. Converting a Julian Day number back to a Gregorian date is accomplished by the function FromJul (Listing 1) . Arguments for FromJul are the Julian Day number, and pointers to the variables which will hold the resultant month, day, and year. With only these two functions it is possible to subtract two dates and add or subtract some number of days to a given date. Once a Gregorian date has been converted to a Julian Day number, it is possible to determine the day of the week that date falls on. The function DayOfWeek (Listing 1) returns an integer ranging from 0 to 7 representing Monday through Sunday, simply by computing the remainder of the Julian Day number divided by 7. The IsLeapYear function in Listing 1 returns a boolean value which indicates whether the year passed as a parameter is a leap year or not.If you have the need to know the phase of the moon on a given date, an approximation can be obtained using the Julian Day number. If the Julian Day number divided by 29.530588 yields a decimal remainder of or proximate to 0.83, that day is a full moon. A decimal remainder proximate to 0.33 indicates a new moon (Harvey 1983).
The conversion routines presented here have been tested using a broad range of dates. Testing has included round tripping every valid date from 4000 B.C. to beyond 5000 A.D., as well as random checking of the day of the week against published calendars. Use caution with B.C. dates. The year immediately preceding 1 A.D. is 1 B.C. There is no year 0. Years prior to 1 A.D. are numbered beginning at -1.
I would like to extend a special thanks to Jeff Betts, president of Creative Programming for providing the references to the algorithms used to create the Julian Day routines in the Vitamin C Library. Without his assistance, the research would have been much more painful.
You may not need routines to manipulate dates every day, but each one of us tries to build a software arsenal which we can use to conquer the daily challenges. These date routines are one more weapon to be added to your cache.
Bibliography
Fliegl, Henry F. and Van Flanders, Thomas C. "A machine algorithm for processing dates." Communications of the ACM, Volume 11, Number 10, October 1968, page 657.Friedman, Howard S. 1989. The Development of the Gregorian Calendar. (This is a text file (kalend.zip) found in the IBM Programmers Forum on CompuServe.)
Harvey, O. L. 1983. Calendar Conversions by Way of the Julian Day Number. Philadelphia, PA: American Philosophical Society.