James Miller G3RUH
All satellite programs involve manipulating dates in some way and if you ever need an example of ugly coding, look no further than the typical amateur calendar routine! I recently came across one Loony program that took over 30 program lines just to manipulate two dates and got it wrong. Yet the job can be done in as few as 5 lines of code. I first encountered date algorithms some years ago in the applications program handbook for the HP25C pocket calculator, and whilst researching this piece they have turned up many places (refs 1,4-6). Where they actually originated I don't know. I shall give the rules for getting from date to day numbers and vice versa in two ways; one where the date is in the familiar 1986-Jan-01 form, and the other in Year, Day-of-year. The latter is so simple that it can be memorised. Day Numbers ----------- The first step to giving a sense of order to calendars is to think in terms of day numbers. All days can have a unique number assigned to them. Then elapsed times are easily found by subtracting day numbers, and calendar routines have a rational system in which to work. Here are some systems I know of: EXAMPLE OF DAY NUMBER System Datum (Day 0) for 1986 Jan 1st ------------------------------------------------ AMSAT 1978 Jan 01 2922 NASA(1) 1957 Sep 18 10332 NASA(2) 1957 Jan 01 10592 ESOC 1950 Jan 01 13149 GENERAL approx AD 1 725022 Julian 4713 BC, Jan 1.5 2446431.5 ----------------------------------------------- The Julian Day numbering system is a universal standard, particularly in astronomic matters because it is the only unambiguously and continously defined system going back to the dark ages. NASA(2) is used in Goddard Space Flight Centre's attitude determination programs; NASA(1) is used in their orbit programs, and is called the Julian Day for Space (JDS). It's time elapsed since the first Julian day divisible by 100 prior to the launch of Sputnik-1. (see ref 3). AMSAT day numbers are telemetered by Oscar-10 and appear in AMSAT-US keplerian element sets (as the time of reference perigee). Note that 1978 Jan 1st is day 0, not 1 as some AMSAT folk would tell you! No system is intrinsically 'the best'. The choice of datum is quite arbitrary and it's obvious that you can hop from one to another merely by adding/subtracting a constant number. For example you can get from a NASA(2) to an ESOC day number by adding 2557, (which is 13149 - 10592). The Calendar Rules ------------------ I suppose Pope Gregory XIII's calendar rules are well known; 365 days to a year; an extra 'leap-day' if the year is divisible by 4, excepting century years - unless they are divisible by 400. 30 days hath September, etc etc and February has 28 days or 29 in a leap year. Thus 1700, 1800, 1900 are NOT leap-years, whereas 1600 and 2000 are. (Ref 2). Leap year days create a 400 year cycle of 146097 days, which is an average of 365.2425 days a year. This is near enough 365.2422 days, the Earth's orbital period about the Sun. If it wasn't - and prior to Gregory (1582) it was not - the seasons would drift. Towards an Algorithm -------------------- Gregory has a lot to answer for! The main reason that calendar algorithmists get in a mess is that February which hath a variable number of days is in the middle of the year. Secondly, the other months have an apparently haphazard mixture of 30/31 days. Basically what we need is a rule to give a day number for the start of a year (DSY), and rule for the start of each month (DSM). Then the day number is simply their sum, viz DAYNO = DSY + DSM + DOM + D0, where DOM is the day of the month and D0 is a datum constant which chosen to suit the day number system adopted (see above). The secret to rational date algorithms is to start the computational 'year' on March 1st, not at the beginning of January. So, 1980-Feb-29 is regarded as 1979, month 12, day 29 (last day of 'year'), and the real 1980-March-01 is regarded as 1980, month 1, day 1. IF M <= 2 THEN M = M+12: Y = Y-1 This trick now gives us a run of 11 months containing 30/31 days, and February now at the end. It's no longer awkward in this position because its days are simply added in to the year + month sub-total, just as for any other month. The 30/31 isn't such a pain in the neck; starting with March, the sequence is 31 30 31 30 31 31 30 31 30 31 31 days. Taking out 30 for clarity it's 1 0 1 0 1 1 0 1 0 1 1 days, which has a discernable 5 month rhythm to it, viz 10101-10101-1. Each bar has 3 beats, an average of 0.6 beats a month. If you now jot down INT(0.6 * M) for M = 0,1,2 etc you get the sequence 0 1 1 2 2 3 4 4 5 .... which increments with the rhythm 10101-101..! Put back in the 30 and, if you're still with me, it should be apparent that the function INT(30.6 * M) provides exactly the basic cadence we need for a start of month rule. Leap Years ---------- Leap years are handled like this; ignoring century years, since there are an average of 365.25 days a year, the day for start of year DSY can be simply found from DSY = INT(year*365.25). Because of the 0.25, every 4th year the leap year increment appears automatically. And what of century years? Easy; every 100 years remove the leap day created above with -INT(year/100), and every 400 years put it back again with +INT(year/400). Day Number to date ------------------ The inverse problem is extracting the date from a day number. Essentially one is unscrambling the rules just given. It's slightly messy because the funtion INT(.) doesn't have an inverse as such, so one has to synthesise it very carefully. WARNING - Ignore This --------------------- INT(X) means "the largest integer smaller than X". Thus INT(-1.5) is -2. Some machines will give -1. The definition given is regular through zero. If your machine gives -1 take great care - and complain to the manufacturer! In addition it is assumed that your computer/calculator can multiply 0.6 by 5, or divide 21 by 7 and get the result 3, not 2.9999999. If it doesn't you may need to take corrective action. John Morris (Reference 6) is excellent on this. ALGORITHM 1: DATE to DAY NUMBER -------------------------------- Takes a date in the form of year, month and day of month and calculates its day number. Valid from 1582 onwards: D0 = -722528: REM For AMSAT day number ) D0 = -428: REM For GENERAL day number )) CHOOSE ONE ONLY D0 = 1720982: REM For Julian Day at noon ) REM Enter wih Year YR e.g. 1986, Month MN, Day DY. Result is Day Number DN Y = YR: M = MN: D = DY: REM Preserve YR, MN, DY IF M <= 2 THEN M = M+12: Y = Y-1 DN = -INT(Y/100)+INT(Y/400)+15 + INT(Y*365.25) + INT((M+1)*30.6) + D + D0 NOTES: 1. You may omit the century calculations so that: DN = INT(Y*365.25) + INT((M+1)*30.6) + D + D0 This restricts the algorithm to 1900 Mar 01 until 2100 Feb 28, 2. Three values for D0 are given; choose only one though! ALGORITHM 2: DAY NUMBER TO DATE -------------------------------- REM Enter with day number (DN). Results are Year (Y), Month (M) and REM Day (D), the day (D$) and month (M$) as strings. D = DN - D0: REM Note 4 DW = (D+5) - 7*INT((D+5)/7): REM Note 1 D = D + INT( INT((D+36387)/36524.25) * 3/4) - 15: REM Note 2 Y = INT((D-122.1)/365.25): D = D-INT(Y*365.25) M = INT(D/30.61): D = D-INT(M*30.6) M = M-1: IF M > 12 THEN M = M-12: Y = Y+1 D$ = MID$("SunMonTueWedThuFriSat",3*DW+1,3): REM Note 1 M$ = MID$("JanFebMarAprMayJunJulAugSepOctNovDec",3*M-2,3): REM Note 3 NOTES: 1. DW is day-of-the-week, and is 0 for Sunday. Omit if you don't need. 2. You may omit this line for dates within 1900 Mar 01 - 2100 Feb 28 3. Omit if you don't want the month in letters. 4. Value for D0 must be as chosen for date to day number algorithm. QUICK ALGORITHMS 3: ------------------- The following two algorithms will give you GENERAL day numbers from the year and day of the year (Jan 1st = 1): Date to Day Number DN = INT((YEAR-1)*365.25) + DAY Day Number to Year/Day of Year YEAR = INT((DN+365)/365.25) DAY = DN - INT((YEAR-1)*365.25) Valid from 1901 Jan 01 - 2100 Dec 31 (General day numbers 693976-767024). The GENERAL day number here is the SAME as for algorithms 1 and 2 above. ISO Date Presentation --------------------- Now the commercial. Does "1st Jan 1986 @ 1432 UTC" or worse, "Jan 1st 1986" strike you as crazy? What you need the International Standards Organistation (ISO-8601) presentation, which has the all the digits in descending order of significance, just like any other number: 1986 Jan 01 @ 1432 UTC Happy New Year de G3RUH REFERENCES ---------- 1. HP19c/HP-29c Applications Book. Hewlett-Packard Company 1977, page 44. 2. Explanatory Supplement to the Astronomical Ephemeris. 1961 Rev. 1974. ISBN: 0-11-880578-9, HMSO. Chapter 14. 3. Spacecraft Attitude Determination and Control. Ed. James R. Wertz, D. Reidel Publishing Co. 1984 ISBN 90-277-1204-2. Page 20. 4. Amateur Satellite Report No. 109 (1985 Sep 14). Note by Tom Clark W3IWI 5. Oscar-10 Program for Sharp PC1246 by Dr Karl Meinzer DJ4ZC 6. Amateur Radio Software, John Morris GM4ANB, RSGB. ISBN: 0-900612-71-1. Definitive, authoritative, stylish and fun. Order one now.
Feedback on these pages to Webmaster. Feedback on the article should be sent to James Miller
Created: 1994 Nov 13 -- Last modified: 2005 Oct 29