<p>This answer offers in two parts, (1) a first thought that might possibly help you to get debugged (but no promises),  and (2) some ways to set up reliable date-conversion without having to 're-invent the wheel'.</p>
<p>(1) Your code adopts Meeus' floating-point-based algorithm, but I see (at least on your linked web-page) that you have included the constant 30.6 . Meeus explains on page 61 that this value, though it would be technically correct in a 'perfect' arithmetic, sometimes gives incorrect rounding/truncation to integer in real-life computer floating-point arithmetic. This can cause 'one-day-off' errors. So he instructs to use 30.601 or 30.6001 instead, to be always on the correct side of each integer boundary for rounding/truncation purposes.</p>
<p>So a first suggestion is that you try the recommended 30.601 or 30.6001 instead of 30.6, and see if that solves any problem.  If not, the debugging will take a little longer.</p>
<p>(2) The programming problem for calendar-to-Julian-day conversions and the reverse was effectively addressed in the 1960s at the JPL (Jet Propulsion Laboratory) -- (and also elsewhere, as in PM2Ring's reference). I suppose that a reason why this wheel has been so often reinvented is the never-ending stream of new programming languages and interface requirements.</p>
<p>The JPL codes to convert between Julian Day Numbers and calendar dates (in both the Julian and Gregorian calendars) rely solely on integer arithmetic. The JPL codes assume separation of date+time combinations into two parts, one a date at Greenwich noon, to be converted with integer arithmetic only, and the other part is a floating-point difference or day-fraction from mean noon, in whatever units may be chosen suitably for the application in hand.</p>
<p>Much of the 'point' of using exclusively integer arithmetic for calendar conversions is that this avoids operations of rounding or truncation, which can give rise to obscure problems and 'one-day-off' errors.</p>
<p>The reliable JPL codes were copied in the 1992 'Explanatory Supplement to the Astronomical Almanac', and as they are quite short and cover both Julian and Gregorian calendars, I include them here below (in the form of page-extract-images, to avoid that I commit any typo errors here).</p>
<p>An alternative worth considering (for Gregorian-form calendar dates only) is to use the calendar subroutines included in the IAU 'Standards of Fundamental Astronomy' program-suite available in C or Fortran at (<a href="http://iausofa.org/" rel="nofollow noreferrer">http://iausofa.org/</a>). (Look for SOFA functions Cal2jd and Jd2cal).</p>
<p>If you continue to suffer from any obscure problem, it's possible that the shortest way in the end could be to install* (or simply port) the JPL or other integer routines into your environment, and test them out on suitable test-data (se below). (* Ensure that you use an integer type big enough to contain the numbers that will be processed, e.g. 'long integer'.)</p>
<p>Otherwise, if you still need to debug your code and can't find a shorter way than the following, I suggest you go through the calculation for two dates, one on the 'right' side and the other on the 'wrong' side of the boundary where your problem date-range begins.   For each date work through <em>both</em> the algorithm that you installed, <em>and</em> the corresponding JPL algorithm, and isolate the point of divergence to localize the error in your code.</p>
<p>First, some sample test data from 'Explanatory Supplement to the Astronomical Ephemeris' (1961). Years before AD 1 are 'astronomical', counted with inclusion of a year '0'. For each year, the given JD is for Greenwich noon on 'January 0.5' (i.e. noon on Dec 31 of the year preceding).</p>
<pre><code>&gt;   Year      JD at Jan 0.5
&gt;             for Julian Jan 0.5:
&gt;   -2000       990557
&gt;   -1900      1027082
&gt; [...]
&gt;    -200      1648007
&gt;    -100      1684532
&gt;       0      1721057
&gt;     100      1757582
&gt; [...]
&gt;              Julian Jan 0.5   Gregorian Jan 0.5
&gt;    1500      2268932          2268923
&gt;    1600      2305457          2305447
&gt;    1900      2415032          2415020
</code></pre>
<p>The following algorithms convert from and to Julian calendar dates:--</p>
<p><a href="https://i.sstatic.net/FJaXZ.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/FJaXZ.png" alt="p.606-code" /></a></p>
<p>The following algorithms convert from and to Gregorian calendar dates:--</p>
<p><a href="https://i.sstatic.net/RaHyv.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/RaHyv.png" alt="p.604-code" /></a></p>

CC BY-SA 4.0
2022-07-08T17:32:40.047Z
Here is a version of this (Gregorian to JD) algorithm I have already implemented:

const jd=Math.trunc((1461*(y+4800+Math.trunc((m-14)/12)))/4)
          +Math.trunc((367*(m-2-12*(Math.trunc((m-14)/12))))/12)
    -Math.trunc((3*Math.trunc((y+4900+Math.trunc((m-14)/12))/100))/4)+d-32075;
Greg Miller
CC BY-SA 4.0
2022-07-08T17:35:24.480Z
Here it is in gist: https://gist.github.com/gmiller123456/b2a1646d6c1f45ad85d6bb871b936c86
Greg Miller
CC BY-SA 4.0
2022-07-08T22:15:35.480Z
@Greg Miller : thanks. That looks right, of course, in the algorithm. I'm not familiar with the particular programming environment.  I didn't post the one I use because it's for a different environment again, doesn't exactly have recognized provenance, and would need a little fiddling with. (And it's so old I even forget what it looks like, it was tested so long ago.) I posted the material that I did only because the OP still had some problem. and a referral to something with known provenance seemed likely to be a way forward.
terry-s
CC BY-SA 4.0
2022-07-09T06:07:49.553Z
@terry Greg's implementation might look a little verbose. That's because JavaScript doesn't have a plain integer type, arithmetic is usually done with floating-point numbers. (However, it does have [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#bigint_type) arbitrary precision integers).
PM 2Ring
CC BY-SA 4.0
2022-07-09T20:00:05.480Z
@PM 2Ring : interesting. not sure why JS would be chosen for this application but maybe there were other factors that led to choosing it.
terry-s
CC BY-SA 4.0
2022-07-09T21:16:50.017Z
Well, JS is probably *not* a great choice to illustrate an algorithm that uses integer arithmetic. ;) However, JS is fairly universal: every modern Web browser has a JavaScript engine, so if you have a Web browser you can run it. JS was created to add scripting capability to HTML, so you can use it to create powerful GUIs using interface elements that are familiar to anyone who surfs the Web. It's a popular choice when people are first learning to program. And although it's an interpreted scripting language it's quite fast.
PM 2Ring
CC BY-SA 4.0
2022-07-10T14:49:15.463Z
@PM 2Ring : Yes, that's true, it's an engine that enables . . . . lots of things ;-| .
terry-s
CC BY-SA 4.0
2022-07-10T16:29:34.510Z
terry-s thanks very much for your answer, to make it short, part of the reason I am re-implementing calculations in Rust is to offer a safe API around calculations (this is a Rust thing BTW), I could go with cffi and use other libs but that would involve calling some "unsafe" code in Rust that sometimes may lead to issue. I updated my question with what I have now, however I am very interested in the calculations you posted about INT only arithmetic which, on paper, should be faster?
I need those to be extremely fast for my final app so I will try the algos you posted and bench them!
Mattia Procopio
CC BY-SA 4.0
2022-07-08T13:35:39.437Z
2022-07-08T14:10:12.377Z
2022-07-08T14:10:12.377Z
terry-s
<p>Sorry, I don't know Rust, so I won't try to debug your code, but it looks like there's some issue with the Julian calendar vs the Gregorian calendar. However, there's an off-by-one error too.</p>
<p>I see that you're using the arithmetic year numbering convention (sometimes known as the astronomical convention), eg 1 AD = year 1, 1 BC = year 0, 2 BC = year -1, etc.</p>
<p>Now -1000 Jul 12.5 Gregorian is JD 1356010, -1000 Jul 12.5 Julian is JD 1356001. But -4712 Jan 1.5 Gregorian is JD 38, -4712 Jan 1.5 Julian is JD 0. Perhaps you're getting 37 instead of 38 due to <a href="https://stackoverflow.com/q/588004/4014959">floating-point issues</a>. I don't know how Rust converts negative floats to ints; some languages round towards zero, some round towards negative infinity.</p>
<p>Personally, I prefer to avoid using floats when the calculations can be done using integer arithmetic.</p>
<p>I see from your profile that you're familiar with Python. I have Python code that does Julian day number conversion for the Gregorian and Julian calendars on <a href="https://gist.github.com/PM2Ring/f078d8d60b7203e423b229fc4afa3a04" rel="nofollow noreferrer">Github</a>. It was derived from RG Tantzen (1963), ACM. Algorithm 199: conversions between calendar date and Julian day number. <a href="https://doi.org/10.1145/366707.390020" rel="nofollow noreferrer">doi: 10.1145/366707.390020</a>.</p>
<p>This code only uses integer arithmetic, and it should work correctly for any date. It has been exhaustively tested against all the dates that the standard Python datetime module accepts.</p>
<p>Here's a <a href="https://sagecell.sagemath.org/?z=eJzNVl1vq0YQfedXjOiDIbYxYDuOkVCbe6NeKVLS6N5KfUgia80uNreGRbAkolH-e2c_bEOcqJXaSrUS4Z09M3PmzOxi27bhutllpABKWiiafM0qEBxXgkHCiydW1RkvLMDPb5nYwjeyYZAVZSMsZbyr-I6VIkvgS8U2vJKhSEFN1BE8S6fLWlS84HmWkB20jFS18s085o0ggMsriJUZArn89Hm_9EcQdpZj3GYi0YmvWJU9MQppxXP4-gV-JYX4gxXgBMvzqTuCy883ngJe7iQtsc0hWC6jTlE1rJl4ZuiDtFhBMYMq-0i_I4qns26FKOtoMmGF95z9npWMZsTj1WYiVxPttUIvBT5V1gdS19mmQN4ostgytVkLUoms2AARUHBegBH8mhQNqVqpymwRTFGJEZQHvU30PXflccufmEoUzpTP7I3PsUcHN91aFEigEOsW7m4g_CrJhH4Yen7ghRdaRkqRtWm_YFVKEmYw515w3pdnk9XC26DozdpLeD65u1ExJ6m_uKAX9NxfL0J_ymbhdB2GyzSZkZRMiT-zbNu2rCwveSWgYpb1A_ySpjUTkPLqVM_aQ4AaS65QMQ6M3PUhq8HHz_hnth6HS2uj6l5pFI5TsAiDIFha33XD3tgXlkVZCsapzelK8NV3WjjtCHLMgH_ZU85prB9upGpv5S7G0EYnhyEscZxDV-_CEKe4hTEEap2M0HQAo2vg-xpJJbJPeAgJnEEwO_eXC5hMYIaWVlsCtVaOFRNNVUh_wOy4O5_i19CViLkuCYuQtRwrcz4ohiI3hyLbHhEXo866JdBjCRhIE3QPAajmeqb4TrUM7_gE73jMJXNlzk885tM3DhIcfNSD8NgDIxAqjRkSpeHQuAzlIaNGJD0U_03fVXf7Y_fPenlk-5e97KXt9_J_3K73m1TiS4SpqvHfFFoSeYYr-6E-c8buj84DHboPV8Pe09YsiUi2Esu8Gt8sydZBXzyRGEq_mVIDwXvklhdMx1c5Krz9HPsTofplgfcSIiO749zhLn33Qhpl7qV_46oLrcGrVCfyNhVvSgern-LF7T72WJjNwD3SkHfHuO3JpDNoddJc7IfWOBlUar-0Efhz-jp-ySM_xAeVD7x1sRyCUV9ejcCy0JU0Ont5denXV7cRrrDidPAi9--lyX6MgL4O3C5Sv22kNpLPmcZqo_3o9qD6an8D1UYFVZRwajUhHP3ObNMsEQ5SiNE8MgrpLPHpfSdd9yAdPz49SBKkMGZyMafXlLLfDvbYOvAIZ_NgPpuj6Sf1TiSJUERXDo4zi7-xHUsEr5wBsjOUTNKBV5e7TDj4Q6U2qJVoSxYPKkIzPhjBjqzZLh4MXKxUszieecUEv8cd-TvD0dOpP7mmoceIWhp5iGJnYCjiYvBBz7ojmEvx--fwsImjW3AhMZEZvcMWkoL4nZfrWS_Am0o7jHWfDGPzk-CE8XF0_jXGp6-Fv8M4bYqklsdKdSnqokawFzYycqDFEI9Muld9EZjhclS0ezlcj-6fRmlVMw==&amp;lang=python" rel="nofollow noreferrer">live version</a> running on the SageMathCell server. When inputting a calendar date, you must give the month in numeric form, but you can use anything non-numeric to separate the year, month &amp; day fields.</p>
<hr />
<p>JPL Horizons provides a calendar date / Julian day number <a href="https://ssd.jpl.nasa.gov/tools/jdc/#/jd" rel="nofollow noreferrer">conversion tool</a>, but it doesn't let you choose a calendar, it just silently switches to the Julian calendar for dates prior to 1582 Oct 15.</p>

CC BY-SA 4.0
2022-07-08T06:03:02.413Z
Thanks for your detailed answer, the off by one error comes from the small if where I remove -1,that is a small leftover from some experiments. The bits about switching is interesting and I don't totalling understand it, BUT I had a look at the source code of libnova (the de facto standard library for calculations) and it seems they do the same, basically if the date is < than 1582.10.04 they use Julian otherwise Gregorian 
Mattia Procopio
CC BY-SA 4.0
2022-07-08T06:07:21.227Z
https://sourceforge.net/p/libnova/libnova/ci/master/tree/src/julian_day.c#l64 for reference
Mattia Procopio
CC BY-SA 4.0
2022-07-08T06:34:21.967Z
@Mattia It's a bit confusing because different countries adopted the Gregorian calendar on different dates. See https://en.wikipedia.org/wiki/Adoption_of_the_Gregorian_calendar & https://en.wikipedia.org/wiki/List_of_adoption_dates_of_the_Gregorian_calendar_per_country Things get complicated when you mix science, politics, and religion. ;)
PM 2Ring
CC BY-SA 4.0
2022-07-10T16:23:05.597Z
indeed, totally agree on the complication
Mattia Procopio
CC BY-SA 4.0
2022-07-07T21:32:36.887Z
2022-07-07T21:32:36.887Z
PM 2Ring
<p>I bought the book <em>Astronomical Algorithms</em> by Jean Meuss and I am reading it in order to build a small library I will use for another project.
I made a program in Rust to calculate the JD given a date see <a href="https://gist.github.com/MattBlack85/b313f5e74c13a3c87462a88af32fb64b" rel="nofollow noreferrer">here</a>, this program uses the following formula from the book:</p>
<p>Given Y as year, M as month and D as day;</p>
<ul>
<li>if M &gt; 2 Y and M are unchanged</li>
<li>if M == 1 or 2 replace Y with Y-1 and M with M+12</li>
</ul>
<p>If the date is from a Gregorian calendar then:</p>
<ul>
<li>calculate <code>A = INT(Y/100)</code> and <code>B = 2 - A + INT(A/4)</code></li>
</ul>
<p>else</p>
<ul>
<li><code>B = 0</code></li>
</ul>
<p>So <code>JD = INT(365.25*(Y+4716)) + INT(30.6001*(M+1)) + D + B - 1524.5</code></p>
<p>The program seems to work correctly for positive dates (see tests) but whenever the date is a negative one the result is wrong; for example JD for -4712 January 1.5 return 37 instead of 0 or JD for -1000 July 12.5 return 1356010 instead of 1356001</p>
<p>I made the calculation also on paper and the program confirms what I get on paper, what is wrong with the formula?</p>
<p>[EDIT]
I reworked my gist using the source code of libnova, the code now works all the time, but I am still confused on why</p>
<p>new gist -&gt; <a href="https://gist.github.com/MattBlack85/c2030dc56c97f5f7fd06dd27a97413bb" rel="nofollow noreferrer">https://gist.github.com/MattBlack85/c2030dc56c97f5f7fd06dd27a97413bb</a>
rust playground -&gt; <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=98e513a4fa196d9e6eccbf6b83509cc2" rel="nofollow noreferrer">https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=98e513a4fa196d9e6eccbf6b83509cc2</a></p>

CC BY-SA 4.0
2022-07-07T20:04:51.980Z
yes, the book by Meeus
Mattia Procopio
CC BY-SA 4.0
2022-07-07T20:54:28.983Z
It looks like there's some issue with Julian calendar vs Gregorian, but there's an off-by-one error too. -1000 Jul 12.5 Gregorian is JD 1356010,  -1000 Jul 12.5 Julian is 1356001. But  -4712 Jan 1.5 Gregorian is JD 38, -4712 Jan 1.5 Julian is JD 0. I guess I should put this in an answer...
PM 2Ring
CC BY-SA 4.0
2022-07-07T22:14:21.510Z
Hi Mattia, I don’t know if you know PHP, but I have converted Meeus’s whole book in PHP. I’d be happy to share. Sorry, I don’t know Rust, and I did it in PHP as it was for my website.
Pierre Paquette
CC BY-SA 4.0
2022-07-07T22:15:35.543Z
One common source of error, is "INT" isn't the normal function implemented in most languages.  Here's a working implementation in JavaScript: https://www.celestialprogramming.com/julian.html
Greg Miller
CC BY-SA 4.0
2022-07-08T00:06:47.143Z
@GregMiller Nice, but your program does the same thing as Horizons: it silently switches to the Julian calendar at JD 2299160.
PM 2Ring
CC BY-SA 4.0
2022-07-08T00:16:13.393Z
The Horizons manual has a [table](https://ssd.jpl.nasa.gov/horizons/manual.html#gregorian-and-julian-calendar-dates) showing the progression near the calendar switch point.
PM 2Ring
CC BY-SA 4.0
2022-07-08T00:35:27.620Z
@PM2Ring, I'm not sure what you're trying to get at, but the entire reason for using Julian days for astronomical algorithms is to have a continuous count of days, uninterrupted.  And this is the behavior expected by the algorithms using Julian days.
Greg Miller
CC BY-SA 4.0
2022-07-08T00:46:35.057Z
@GregMiller If I put JD 0 into your Web page it says that it's Gregorian date -4712 1 1.5. But JD 0 is actually -4712 1 1.5 in the (proleptic) Julian calendar. In the (proleptic) Gregorian calendar JD 0 is -4713 11 24.5
PM 2Ring
CC BY-SA 4.0
2022-07-08T01:51:09.570Z
Whether or not the values match up to actual calendars used historically isn't the goal of this algorithm.  Meeus spends a couple of pages explaining exactly what is meant, and I believe there is an IAU resolution detailing all of the specifics.
Greg Miller
CC BY-SA 4.0
2022-07-08T06:15:10.907Z
Also folks, correct me if I am wrong, but as far as I understand the book -4712 is indeed the first year when counting Julian days as it means 4713BC
Mattia Procopio
CC BY-SA 4.0
2022-07-08T09:35:54.020Z
@Mattia Yes. From https://en.wikipedia.org/wiki/Julian_day Julian day number 0 [is] assigned to the day starting at noon on Monday, January 1, 4713 BC, proleptic Julian calendar (November 24, 4714 BC, in the proleptic Gregorian calendar).
PM 2Ring
CC BY-SA 4.0
2022-07-08T14:36:26.890Z
I just looked at your code, and there is no use of the INT function.  On P60, Meeus explains how that function is supposed to work, so make sure it's getting applied correctly.  You are also using 30.6 rather than 30.6001.
Greg Miller
CC BY-SA 4.0
2022-07-10T16:19:02.227Z
That is correct Greg, however it seems using Rust 30.6 is just fine, however I made a new gist (with attached playground with 30.6001
Mattia Procopio
<algorithm>
Calculation of Julian day is off for negative dates
Mattia Procopio
# Calculation of Julian day is off for negative dates

(Asked by: username_0 on 2022-07-07)

I bought the book *Astronomical Algorithms* by Jean Meuss and I am reading it in order to build a small library I will use for another project.
I made a program in Rust to calculate the JD given a date see [here](https://gist.github.com/MattBlack85/b313f5e74c13a3c87462a88af32fb64b), this program uses the following formula from the book:

Given Y as year, M as month and D as day;

* if M > 2 Y and M are unchanged
* if M == 1 or 2 replace Y with Y-1 and M with M+12

If the date is from a Gregorian calendar then:

* calculate `A = INT(Y/100)` and `B = 2 - A + INT(A/4)`

else

* `B = 0`

So `JD = INT(365.25*(Y+4716)) + INT(30.6001*(M+1)) + D + B - 1524.5`

The program seems to work correctly for positive dates (see tests) but whenever the date is a negative one the result is wrong; for example JD for -4712 January 1.5 return 37 instead of 0 or JD for -1000 July 12.5 return 1356010 instead of 1356001

I made the calculation also on paper and the program confirms what I get on paper, what is wrong with the formula?

[EDIT]
I reworked my gist using the source code of libnova, the code now works all the time, but I am still confused on why

new gist -> <https://gist.github.com/MattBlack85/c2030dc56c97f5f7fd06dd27a97413bb>
rust playground -> <https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=98e513a4fa196d9e6eccbf6b83509cc2>

**Tags:** algorithm

> **Comments:**
> **username_0**: yes, the book by Meeus  
> **username_1**: It looks like there's some issue with Julian calendar vs Gregorian, but there's an off-by-one error too. -1000 Jul 12.5 Gregorian is JD 1356010,  -1000 Jul 12.5 Julian is 1356001. But  -4712 Jan 1.5 Gregorian is JD 38, -4712 Jan 1.5 Julian is JD 0. I guess I should put this in an answer...  
> **username_2**: Hi username_0, I don’t know if you know PHP, but I have converted Meeus’s whole book in PHP. I’d be happy to share. Sorry, I don’t know Rust, and I did it in PHP as it was for my website.  
> **username_3**: One common source of error, is "INT" isn't the normal function implemented in most languages.  Here's a working implementation in JavaScript: https://www.celestialprogramming.com/julian.html  
> **username_1**: @username_3 Nice, but your program does the same thing as Horizons: it silently switches to the Julian calendar at JD 2299160.  
> **username_1**: The Horizons manual has a [table](https://ssd.jpl.nasa.gov/horizons/manual.html#gregorian-and-julian-calendar-dates) showing the progression near the calendar switch point.  
> **username_3**: @username_1, I'm not sure what you're trying to get at, but the entire reason for using Julian days for astronomical algorithms is to have a continuous count of days, uninterrupted.  And this is the behavior expected by the algorithms using Julian days.  
> **username_1**: @username_3 If I put JD 0 into your Web page it says that it's Gregorian date -4712 1 1.5. But JD 0 is actually -4712 1 1.5 in the (proleptic) Julian calendar. In the (proleptic) Gregorian calendar JD 0 is -4713 11 24.5  
> **username_3**: Whether or not the values match up to actual calendars used historically isn't the goal of this algorithm.  Meeus spends a couple of pages explaining exactly what is meant, and I believe there is an IAU resolution detailing all of the specifics.  
> **username_0**: Also folks, correct me if I am wrong, but as far as I understand the book -4712 is indeed the first year when counting Julian days as it means 4713BC  
> **username_1**: @username_0 Yes. From https://en.wikipedia.org/wiki/Julian_day Julian day number 0 [is] assigned to the day starting at noon on Monday, January 1, 4713 BC, proleptic Julian calendar (November 24, 4714 BC, in the proleptic Gregorian calendar).  
> **username_3**: I just looked at your code, and there is no use of the INT function.  On P60, Meeus explains how that function is supposed to work, so make sure it's getting applied correctly.  You are also using 30.6 rather than 30.6001.  
> **username_0**: That is correct username_3, however it seems using Rust 30.6 is just fine, however I made a new gist (with attached playground with 30.6001  

---

## 2 answers

### Answer by username_4 on 2022-07-08. Score: 4 | Accepted answer

This answer offers in two parts, (1) a first thought that might possibly help you to get debugged (but no promises), and (2) some ways to set up reliable date-conversion without having to 're-invent the wheel'.

(1) Your code adopts Meeus' floating-point-based algorithm, but I see (at least on your linked web-page) that you have included the constant 30.6 . Meeus explains on page 61 that this value, though it would be technically correct in a 'perfect' arithmetic, sometimes gives incorrect rounding/truncation to integer in real-life computer floating-point arithmetic. This can cause 'one-day-off' errors. So he instructs to use 30.601 or 30.6001 instead, to be always on the correct side of each integer boundary for rounding/truncation purposes.

So a first suggestion is that you try the recommended 30.601 or 30.6001 instead of 30.6, and see if that solves any problem. If not, the debugging will take a little longer.

(2) The programming problem for calendar-to-Julian-day conversions and the reverse was effectively addressed in the 1960s at the JPL (Jet Propulsion Laboratory) -- (and also elsewhere, as in username_1's reference). I suppose that a reason why this wheel has been so often reinvented is the never-ending stream of new programming languages and interface requirements.

The JPL codes to convert between Julian Day Numbers and calendar dates (in both the Julian and Gregorian calendars) rely solely on integer arithmetic. The JPL codes assume separation of date+time combinations into two parts, one a date at Greenwich noon, to be converted with integer arithmetic only, and the other part is a floating-point difference or day-fraction from mean noon, in whatever units may be chosen suitably for the application in hand.

Much of the 'point' of using exclusively integer arithmetic for calendar conversions is that this avoids operations of rounding or truncation, which can give rise to obscure problems and 'one-day-off' errors.

The reliable JPL codes were copied in the 1992 'Explanatory Supplement to the Astronomical Almanac', and as they are quite short and cover both Julian and Gregorian calendars, I include them here below (in the form of page-extract-images, to avoid that I commit any typo errors here).

An alternative worth considering (for Gregorian-form calendar dates only) is to use the calendar subroutines included in the IAU 'Standards of Fundamental Astronomy' program-suite available in C or Fortran at (<http://iausofa.org/>). (Look for SOFA functions Cal2jd and Jd2cal).

If you continue to suffer from any obscure problem, it's possible that the shortest way in the end could be to install\* (or simply port) the JPL or other integer routines into your environment, and test them out on suitable test-data (se below). (\* Ensure that you use an integer type big enough to contain the numbers that will be processed, e.g. 'long integer'.)

Otherwise, if you still need to debug your code and can't find a shorter way than the following, I suggest you go through the calculation for two dates, one on the 'right' side and the other on the 'wrong' side of the boundary where your problem date-range begins. For each date work through *both* the algorithm that you installed, *and* the corresponding JPL algorithm, and isolate the point of divergence to localize the error in your code.

First, some sample test data from 'Explanatory Supplement to the Astronomical Ephemeris' (1961). Years before AD 1 are 'astronomical', counted with inclusion of a year '0'. For each year, the given JD is for Greenwich noon on 'January 0.5' (i.e. noon on Dec 31 of the year preceding).

```
>   Year      JD at Jan 0.5
>             for Julian Jan 0.5:
>   -2000       990557
>   -1900      1027082
> [...]
>    -200      1648007
>    -100      1684532
>       0      1721057
>     100      1757582
> [...]
>              Julian Jan 0.5   Gregorian Jan 0.5
>    1500      2268932          2268923
>    1600      2305457          2305447
>    1900      2415032          2415020

```

The following algorithms convert from and to Julian calendar dates:--

[![p.606-code](https://i.sstatic.net/FJaXZ.png)](https://i.sstatic.net/FJaXZ.png)

The following algorithms convert from and to Gregorian calendar dates:--

[![p.604-code](https://i.sstatic.net/RaHyv.png)](https://i.sstatic.net/RaHyv.png)

> **Comments:**
> **username_3**: Here is a version of this (Gregorian to JD) algorithm I have already implemented:  const jd=Math.trunc((1461*(y+4800+Math.trunc((m-14)/12)))/4)           +Math.trunc((367*(m-2-12*(Math.trunc((m-14)/12))))/12)     -Math.trunc((3*Math.trunc((y+4900+Math.trunc((m-14)/12))/100))/4)+d-32075;  
> **username_3**: Here it is in gist: https://gist.github.com/gmiller123456/b2a1646d6c1f45ad85d6bb871b936c86  
> **username_4**: @username_3 : thanks. That looks right, of course, in the algorithm. I'm not familiar with the particular programming environment.  I didn't post the one I use because it's for a different environment again, doesn't exactly have recognized provenance, and would need a little fiddling with. (And it's so old I even forget what it looks like, it was tested so long ago.) I posted the material that I did only because the OP still had some problem. and a referral to something with known provenance seemed likely to be a way forward.  
> **username_1**: @terry username_3's implementation might look a little verbose. That's because JavaScript doesn't have a plain integer type, arithmetic is usually done with floating-point numbers. (However, it does have [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#bigint_type) arbitrary precision integers).  
> **username_4**: @username_1 : interesting. not sure why JS would be chosen for this application but maybe there were other factors that led to choosing it.  
> **username_1**: Well, JS is probably *not* a great choice to illustrate an algorithm that uses integer arithmetic. ;) However, JS is fairly universal: every modern Web browser has a JavaScript engine, so if you have a Web browser you can run it. JS was created to add scripting capability to HTML, so you can use it to create powerful GUIs using interface elements that are familiar to anyone who surfs the Web. It's a popular choice when people are first learning to program. And although it's an interpreted scripting language it's quite fast.  
> **username_4**: @username_1 : Yes, that's true, it's an engine that enables . . . . lots of things ;-| .  
> **username_0**: username_4 thanks very much for your answer, to make it short, part of the reason I am re-implementing calculations in Rust is to offer a safe API around calculations (this is a Rust thing BTW), I could go with cffi and use other libs but that would involve calling some "unsafe" code in Rust that sometimes may lead to issue. I updated my question with what I have now, however I am very interested in the calculations you posted about INT only arithmetic which, on paper, should be faster? I need those to be extremely fast for my final app so I will try the algos you posted and bench them!  

---

### Answer by username_1 on 2022-07-07. Score: 2

Sorry, I don't know Rust, so I won't try to debug your code, but it looks like there's some issue with the Julian calendar vs the Gregorian calendar. However, there's an off-by-one error too.

I see that you're using the arithmetic year numbering convention (sometimes known as the astronomical convention), eg 1 AD = year 1, 1 BC = year 0, 2 BC = year -1, etc.

Now -1000 Jul 12.5 Gregorian is JD 1356010, -1000 Jul 12.5 Julian is JD 1356001. But -4712 Jan 1.5 Gregorian is JD 38, -4712 Jan 1.5 Julian is JD 0. Perhaps you're getting 37 instead of 38 due to [floating-point issues](https://stackoverflow.com/q/588004/4014959). I don't know how Rust converts negative floats to ints; some languages round towards zero, some round towards negative infinity.

Personally, I prefer to avoid using floats when the calculations can be done using integer arithmetic.

I see from your profile that you're familiar with Python. I have Python code that does Julian day number conversion for the Gregorian and Julian calendars on [Github](https://gist.github.com/username_1/f078d8d60b7203e423b229fc4afa3a04). It was derived from RG Tantzen (1963), ACM. Algorithm 199: conversions between calendar date and Julian day number. [doi: 10.1145/366707.390020](https://doi.org/10.1145/366707.390020).

This code only uses integer arithmetic, and it should work correctly for any date. It has been exhaustively tested against all the dates that the standard Python datetime module accepts.

Here's a [live version](https://sagecell.sagemath.org/?z=eJzNVl1vq0YQfedXjOiDIbYxYDuOkVCbe6NeKVLS6N5KfUgia80uNreGRbAkolH-e2c_bEOcqJXaSrUS4Z09M3PmzOxi27bhutllpABKWiiafM0qEBxXgkHCiydW1RkvLMDPb5nYwjeyYZAVZSMsZbyr-I6VIkvgS8U2vJKhSEFN1BE8S6fLWlS84HmWkB20jFS18s085o0ggMsriJUZArn89Hm_9EcQdpZj3GYi0YmvWJU9MQppxXP4-gV-JYX4gxXgBMvzqTuCy883ngJe7iQtsc0hWC6jTlE1rJl4ZuiDtFhBMYMq-0i_I4qns26FKOtoMmGF95z9npWMZsTj1WYiVxPttUIvBT5V1gdS19mmQN4ostgytVkLUoms2AARUHBegBH8mhQNqVqpymwRTFGJEZQHvU30PXflccufmEoUzpTP7I3PsUcHN91aFEigEOsW7m4g_CrJhH4Yen7ghRdaRkqRtWm_YFVKEmYw515w3pdnk9XC26DozdpLeD65u1ExJ6m_uKAX9NxfL0J_ymbhdB2GyzSZkZRMiT-zbNu2rCwveSWgYpb1A_ySpjUTkPLqVM_aQ4AaS65QMQ6M3PUhq8HHz_hnth6HS2uj6l5pFI5TsAiDIFha33XD3tgXlkVZCsapzelK8NV3WjjtCHLMgH_ZU85prB9upGpv5S7G0EYnhyEscZxDV-_CEKe4hTEEap2M0HQAo2vg-xpJJbJPeAgJnEEwO_eXC5hMYIaWVlsCtVaOFRNNVUh_wOy4O5_i19CViLkuCYuQtRwrcz4ohiI3hyLbHhEXo866JdBjCRhIE3QPAajmeqb4TrUM7_gE73jMJXNlzk885tM3DhIcfNSD8NgDIxAqjRkSpeHQuAzlIaNGJD0U_03fVXf7Y_fPenlk-5e97KXt9_J_3K73m1TiS4SpqvHfFFoSeYYr-6E-c8buj84DHboPV8Pe09YsiUi2Esu8Gt8sydZBXzyRGEq_mVIDwXvklhdMx1c5Krz9HPsTofplgfcSIiO749zhLn33Qhpl7qV_46oLrcGrVCfyNhVvSgern-LF7T72WJjNwD3SkHfHuO3JpDNoddJc7IfWOBlUar-0Efhz-jp-ySM_xAeVD7x1sRyCUV9ejcCy0JU0Ont5denXV7cRrrDidPAi9--lyX6MgL4O3C5Sv22kNpLPmcZqo_3o9qD6an8D1UYFVZRwajUhHP3ObNMsEQ5SiNE8MgrpLPHpfSdd9yAdPz49SBKkMGZyMafXlLLfDvbYOvAIZ_NgPpuj6Sf1TiSJUERXDo4zi7-xHUsEr5wBsjOUTNKBV5e7TDj4Q6U2qJVoSxYPKkIzPhjBjqzZLh4MXKxUszieecUEv8cd-TvD0dOpP7mmoceIWhp5iGJnYCjiYvBBz7ojmEvx--fwsImjW3AhMZEZvcMWkoL4nZfrWS_Am0o7jHWfDGPzk-CE8XF0_jXGp6-Fv8M4bYqklsdKdSnqokawFzYycqDFEI9Muld9EZjhclS0ezlcj-6fRmlVMw==&lang=python) running on the SageMathCell server. When inputting a calendar date, you must give the month in numeric form, but you can use anything non-numeric to separate the year, month & day fields.

---

JPL Horizons provides a calendar date / Julian day number [conversion tool](https://ssd.jpl.nasa.gov/tools/jdc/#/jd), but it doesn't let you choose a calendar, it just silently switches to the Julian calendar for dates prior to 1582 Oct 15.

> **Comments:**
> **username_0**: Thanks for your detailed answer, the off by one error comes from the small if where I remove -1,that is a small leftover from some experiments. The bits about switching is interesting and I don't totalling understand it, BUT I had a look at the source code of libnova (the de facto standard library for calculations) and it seems they do the same, basically if the date is < than 1582.10.04 they use Julian otherwise Gregorian   
> **username_0**: https://sourceforge.net/p/libnova/libnova/ci/master/tree/src/julian_day.c#l64 for reference  
> **username_1**: @username_0 It's a bit confusing because different countries adopted the Gregorian calendar on different dates. See https://en.wikipedia.org/wiki/Adoption_of_the_Gregorian_calendar & https://en.wikipedia.org/wiki/List_of_adoption_dates_of_the_Gregorian_calendar_per_country Things get complicated when you mix science, politics, and religion. ;)  
> **username_0**: indeed, totally agree on the complication  

---

