30.6 DAYS HATH SEPTEMBER
------------------------
by 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
memorized.
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 continuously
defined system going back to the dark ages. NASA(2) is used
in Goddard
Space Flight Center’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
function INT(.) doesn't have an inverse as such, so one has to
synthesize
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 with 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
Organization (ISO) 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.