NavList:
A Community Devoted to the Preservation and Practice of Celestial Navigation and Other Methods of Traditional Wayfinding
Re: Julian Day Number algorithms
From: Paul Hirose
Date: 2024 Mar 20, 13:12 -0700
From: Paul Hirose
Date: 2024 Mar 20, 13:12 -0700
There seems to be a rule that calendar algorithms are not allowed to use
any obvious methods such as table lookup, branching, or iteration.
Tradition requires clever hacks with minimum lines of code.
Here I violate tradition with a Julian day number algorithm designed for
easy understanding. It's valid for any BC or AD date in the Gregorian or
Julian calendar. The time span is limited only by integer overflow.
I begin with conversion from calendar date to GI (Gregorian integer),
where GI 0 is the last day of year 0 (1 BC) in the Gregorian proleptic
calendar. Days are numbered in sequence in both directions from that
date: GI -1 is year 0 Dec 30 and GI +1 is year 1 Jan 1.
In this algorithm the year number (y) uses the astronomical convention
where 1 BC = 0, 2 BC = -1. etc. All divisions use integer math. For
example, 2024 / 100 is exactly 20.
1. If an AD year, decrement y. If BC, reverse the sign of y.
2. Compute GI at the "0th" day of this year (last day of previous year).
If the year is BC the formula gives a wrong result, but that's corrected
in the next step. Remember, in integer division the remainder is discarded.
GI = y * 365 + y / 4 - y / 100 + y / 400
3. If BC, add 366 and negate.
For example, in year 0 (1 BC), step 2 is zero. Adding 366 and reversing
the sign gives -366, which is the correct GI at the 0th day of year 0
(which has 366 days according to the leap year rules).
In similar fashion we can validate the result in year -1 (2 BC): step 2
is 365, corrected to -731 in step 3. That's the GI at the 0th day of
year -1.
4. At this point we have the GI at the 0th year of the year. Now add the
days in the whole months. E.g., for a March date add 31 and 28 (or 29 if
a leap year).
5. At this point we have the GI at the 0th day of this month. Add day of
month.
For example, calculate GI at 2024 March 19. Per step 1, adjust y to
2023. Step 2 is GI = 2023 * 365 + 2023 / 4 - 2023 / 100 + 2023 / 400 =
738 885. Step 3 does not apply since the date is AD. Step 4 adds 31 and
29. In step 5 add 19 to get 738 964 for the GI.
For dates in the Julian calendar a Julian integer can be calculated with
a similar method. In step 2 use the formula JI = y * 365 + y / 4. In
step 4 use Julian rules to determine the number of days in February.
For example, calculate JI at 2024 March 6 (equivalent to the Gregorian
calendar date just calculated). Step 2 is JI = 2023 * 365 + 2023 / 4 =
738 900. Add 31 and 29 for January and February. Add 6 for day of month.
JI = 738 966, or 2 more than GI on the same day.
Since GI and JI proceed in parallel into the past and future, JI is
always GI + 2. GI and JI also have constant differences from JDN (Julian
day number).
To determine the differences, calculate JI at the JDN zero point: -4712
Jan 1, Julian calendar. Per step 1, change -4712 to +4712. Step 2 is JI
= 4712 * 365 + 4712 / 4 = 1 721 058. Per step 3, add 366 and negate.
Result is -1 721 424. Step 4 is not applicable since there are no whole
months before a January date. In step 5 add 1 for day of month to get JI
-1 721 423.
Thus, JDN 0 = JI -1 721 423. And since GI is always 2 less than JI, we
can also say that JDN 0 = GI -1 721 425. Or to put it another way, JDN =
GI + 1 721 425. To verify, convert the previously calculated GI (738 964
at 2024 March 19) to JDN. Result is JDN 2 460 389. That agrees with the
table in Almanac section K:
https://archive.org/details/binder1_202003/page/n539/mode/1up?view=theater
In a computer implementation I use a 2-dimensional array in step 4:
{{31, 28, 31...}, {31, 29, 31...}} and iterate to add the days in the
whole months. (With the same array you can verify day of month does not
exceed the legal range.) To select the correct row I wrote function
IsLeapYear(y, julian). The "julian" parameter is true for Julian
calendar dates. The function returns 0 if y is a normal year or 1 if
leap. That value indexes into the correct row of the array. (The year
passed to the function must be the actual value, before it's modified in
step 1.)
I'll explain the reverse conversion (JDN to calendar date) in a followup.
--
Paul Hirose
sofajpl.com






