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 25, 13:46 -0700
From: Paul Hirose
Date: 2024 Mar 25, 13:46 -0700
In my previous message on the 20th I introduced the GI (Gregorian integer) and JI (Julian integer), which are day number systems with their zeros at year 1 December 31 in the respective calendars. There is also JDN (Julian day number) with its zero at -4712 January 1, Julian calendar. I showed that: JI = GI + 2 JDN = GI + 1 721 425 JDN 2 460 389 = GI 738 964 = 2024 March 19 JDN 2 460 389 = JI 738 966 = 2024 March 6 (Julian calendar) Now we look at the conversion from JDN to Gregorian calendar date. With modifications the algorithm works for the Julian calendar too. 1. Calculate GI = JDN - 1 721 425. 2. With integer math, calculate the number of whole 400-year Gregorian cycles in GI, and the remainder after the whole cycles are removed. (The Julian cycle is only 4 years or 1461 days.) cycles = GI / 146 097 remainder = GI - cycles * 146 097 (In the C language you can use the remainder operator %. Fortran has a REMAINDER function.) 3. Assemble those values into an estimated year: y = cycles * 400 + remainder * 400 / 146 097 4. If GI > 0, increment y. Otherwise subtract -1 / 2 (computed with integer division) from y. 5. Compute GI0, the GI at the last day of preceding year, with the GI algorithm (steps 1, 2, 3) in my previous message. 6. Compute day of year d: d = GI - GI0. 7. If d < 1, decrement y. If d > 365 (or 366 if leap year), increment y. If either correction is necessary, repeat steps 5 and 6 to re-calculate d. 8. Convert day of year d to calendar date: subtract days in whole months, then what remains is day of month. EXAMPLE Earlier I converted 2024 March 19 to JDN 2 460 389. Now do the reverse. In step 1 subtract 1 721 425 from JDN to get GI 738 964. Step 2 gives 5 cycles and remainder 8479. Step 3 is y = 400 * 5 + 8479 * 400 / 146 097 = 2023. In step 4 increment y to 2024. Step 5 gives 738 885 for GI0. Step 6 gives 79 for day of year. Step 7 does not call for a recomputation of y. In step 8 subtract 31, then 29 to get 19, the March date. Result is 2024 March 19, the correct date. For a more difficult example, convert JDN 0 to Gregorian calendar date. Step 1 gives GI -1 721 425. In step 2 there are -11 cycles (assume division truncates toward 0) and remainder -114358. In step 3, y = -11 * 400 + -114358 * 400 / 146097 = -4713. Step 4 has no effect since -1 / 2 = 0 if division truncates toward 0. In step 5, GI0 = -(4713 * 365 + 4713 / 4 - 4713 / 100 + 4713 / 400 + 366) = -1 721 753. In step 6, day of year d = GI - GI0 = 328. In step 7 it's not necessary to adjust y and re-compute since d is already in the legal range. In step 8, after you subtract the days in Jan - Oct, 24 remains. Thus the date is -4713 Nov 24 in the Gregorian calendar. The conversion from JDN to Julian calendar date is similar. The Julian cycle is only 4 years (1461 days). EXPLANATION The obvious way to estimate y with integer math is to divide GI by the mean length of a Gregorian year: 146 097 / 400. Thus, y = GI * 400 / 146 097. However, the range of that formula is limited by overflow in the multiplication by 400. The maximum value of a 32-bit integer is about 2 billion. Dividing by 400 gives 5 million days or about 14 000 years as the maximum epoch that avoids overflow. This is extreme as calendar algorithms go, but some recent JPL ephemerides go out about that far. Besides, I was not willing to accept an algorithm with 1/400 the range it might have had, if not for one operation. The solution to to first remove whole multiples of the 400-year Gregorian cycle of 146 097 days (400 * 365 + 100 - 4 + 1). The remainder is small enough that multiplication by 400 / 146 097 converts it to years without danger of overflow. In step 3, y is one less than the correct year for AD dates. For BC dates it depends on what happens in integer division when one operand is negative. The C language allows truncation in the direction that is most natural on the hardware. If division truncates away from 0 (-3/2 = -2), y is one less than the correct year. If it truncates toward 0 (-3/2 = -1), y is the correct year. (Walk through the computation of when GI = +180 and -180 to satisfy yourself on this.) The apparently peculiar action of subtracting -1 / 2 in step 4 applies a correction for truncation. A similar precaution is not necessary in the computation of cycles in step 2 because the math is valid regardless of truncation direction. On a calculator you can simulate integer division with the INT function, which drops the fractional part of a number. This is the same thing as truncation toward 0, and so you can omit the subtraction of -1 / 2 for BC dates in step 4. Step 5 verifies correct y. The value estimated in the preceding steps can be "off by 1" on the first or last day of a year. E.g., at +4 Dec 31 it's too high, and at +204 Jan 1 it's too low. In a programmed implementation of step 5 it's convenient to have a function DaysInThisYear(y, julian). The function returns either 365 or 366 according to y and whether or not the "julian" parameter is true. -- Paul Hirose sofajpl.com