# Julian dates for proleptic Gregorian and Julian calendars

The Python module jdcal provides a few functions for calculating Julian dates for Gregorian and Julian calendar dates. Julian dates are stored as two floating point numbers for maximum precision.

Contents

# Installation

The module can be installed using `easy_install` or `pip`.

`$ pip install jdcal`

or,

`$ easy_install jdcal`

The module can be downloaded from the pypi page for the module.

The source code repository is at http://github.com/phn/jdcal .

The module is available under the BSD license (http://www.opensource.org/licenses/bsd-license.php).

# Functions

The function `gcal2jd` accepts the year, month and day of a Gregorian calendar date and returns corresponding Julian date. The function `jcal2jd` accepts the year, month and day of a Julian calendar date and returns the corresponding Julian date. Functions `jd2gcal` and `jd2jcal` take Julian dates and returns Gregorian and Julian calendar dates, respectively.

Different regions of the world switched to Gregorian calendar from Julian calendar on different dates. Having separate functions for Julian and Gregorian calendars allow maximum flexibility in choosing the relevant calendar.

All the above functions are “proleptic”. This means that they work for dates on which the concerned calendar is not valid. For example, Gregorian calendar was not used prior to around October 1582.

Julian dates are stored in two floating point numbers (double). Julian dates, and Modified Julian dates, are large numbers. If only one number is used, then the precision of the time stored is limited. Using two numbers, time can be split in a manner that will allow maximum precision. For example, the first number could be the Julian date for the beginning of a day and the second number could be the fractional day. Calculations that need the latter part can now work with maximum precision.

The function `is_leap` can be used to test whether a given Gregorian calendar year is a leap year or not.

Zero point of Modified Julian Date (MJD) and the MJD of 2000/1/1 12:00:00 are defined as `MJD_0` and `MJD_J2000` respectively.

This module is based on the TPM C library, by Jeffery W. Percival.

# Examples

## Gregorian calendar to Julian dates

Function `gcal2jd` returns the Julian date for midnight of the given Calendar date.

```
>>> gcal2jd(2000,1,1)
(2400000.5, 51544.0)
>>> 2400000.5 + 51544.0 + 0.5
2451545.0
```

Negative months and days are valid. For example, 2000/-2/-4 => 1999/+12-2/-4 => 1999/10/-4 => 1999/9/30-4 => 1999/9/26.

```
>>> gcal2jd(2000, -2, -4)
(2400000.5, 51447.0)
>>> gcal2jd(1999, 9, 26)
(2400000.5, 51447.0)
>>> gcal2jd(2000, 2, -1)
(2400000.5, 51573.0)
>>> gcal2jd(2000, 1, 30)
(2400000.5, 51573.0)
>>> gcal2jd(2000, 3, -1)
(2400000.5, 51602.0)
>>> gcal2jd(2000, 2, 28)
(2400000.5, 51602.0)
```

Month 0 becomes previous month.

```
>>> gcal2jd(2000, 0, 1)
(2400000.5, 51513.0)
>>> gcal2jd(1999, 12, 1)
(2400000.5, 51513.0)
```

Day number 0 becomes last day of previous month.

```
>>> gcal2jd(2000, 3, 0)
(2400000.5, 51603.0)
>>> gcal2jd(2000, 2, 29)
(2400000.5, 51603.0)
```

If `day` is greater than the number of days in `month`, then it gets carried over to the next month.

```
>>> gcal2jd(2000,2,30)
(2400000.5, 51604.0)
>>> gcal2jd(2000,3,1)
(2400000.5, 51604.0)
>>> gcal2jd(2001,2,30)
(2400000.5, 51970.0)
>>> gcal2jd(2001,3,2)
(2400000.5, 51970.0)
```

Function `jd2gcal` converts Julian dates to Gregorian calendar dates. The values returned are year, month, day and fraction of the day.

```
>>> jd2gcal(*gcal2jd(2000,1,1))
(2000, 1, 1, 0.0)
>>> jd2gcal(*gcal2jd(1950,1,1))
(1950, 1, 1, 0.0)
>>> gcal2jd(2000,1,1)
(2400000.5, 51544.0)
>>> jd2gcal(2400000.5, 51544.0)
(2000, 1, 1, 0.0)
>>> jd2gcal(2400000.5, 51544.5)
(2000, 1, 1, 0.5)
>>> jd2gcal(2400000.5, 51544.245)
(2000, 1, 1, 0.24500000000261934)
>>> jd2gcal(2400000.5, 51544.1)
(2000, 1, 1, 0.099999999998544808)
>>> jd2gcal(2400000.5, 51544.75)
(2000, 1, 1, 0.75)
```

Out of range months and days are carried over to the next/previous year or next/previous month.

```
>>> jd2gcal(*gcal2jd(1999,10,12))
(1999, 10, 12, 0.0)
>>> jd2gcal(*gcal2jd(2000,2,30))
(2000, 3, 1, 0.0)
>>> jd2gcal(*gcal2jd(-1999,10,12))
(-1999, 10, 12, 0.0)
>>> jd2gcal(*gcal2jd(2000, -2, -4))
(1999, 9, 26, 0.0)
```

## Julian calendar to Julian dates

Julian calendar dates can be converted to Julian dates using `jcal2jd`. Unlike `gcal2jd`, negative months and days can result in incorrect Julian dates.

```
>>> jcal2jd(2000, 1, 1)
(2400000.5, 51557.0)
```

The function `jd2jcal` converts Julian dates to Julian calendar dates.

```
>>> jd2jcal(*jcal2jd(2000, 1, 1))
(2000, 1, 1, 0.0)
>>> jd2jcal(*jcal2jd(-4000, 10, 11))
(-4000, 10, 11, 0.0)
>>> jcal2jd(2000, 1, 1)
(2400000.5, 51557.0)
>>> jd2jcal(2400000.5, 51557.0)
(2000, 1, 1, 0.0)
>>> jd2jcal(2400000.5, 51557.5)
(2000, 1, 1, 0.5)
>>> jd2jcal(2400000.5, 51557.245)
(2000, 1, 1, 0.24500000000261934)
>>> jd2jcal(2400000.5, 51557.1)
(2000, 1, 1, 0.099999999998544808)
>>> jd2jcal(2400000.5, 51557.75)
(2000, 1, 1, 0.75)
```

## Leap years

Use the function `is_leap` to determine whether a Gregorian calendar year is a leap year or not.

```
>>> is_leap(2000)
True
>>> is_leap(2100)
False
```

## Constants

The zero point of Modified Julian Date (MJD) is stored in `MJD_0`. The MJD for 2000/1/1 12:00:00 is stored in `MJD_J2000`.

```
>>> print MJD_0
2400000.5
>>> print MJD_JD2000
51544.5
```

Could you include a decimal-gregorian-year to JD function? There are some astro catalogs that report dates as, e.g., 2007.2392876. I’m not sure how to convert that to y/m/d using jdcal, though I’ll figure it out using python’s date.

[Github Gist: https://gist.github.com/2594168. Code is not showing up properly in the comment.]

The following functions should do the job. With your example date:

In [75]: gyear2gcal(2007.2392876)

Out[75]: (2007, 3, 28, 0.33997399996587774)

In [76]: gcal2gyear(2007,3,28) + (_[-1]/ days_in_year(2007))

Out[76]: 2007.2392876

But I haven’t tested it extensively. It would be great if you could compare this with any other method you use. Until I test this I don’t want to include it in jdcal.

Especially, I am confused about there being day 0 while calculating decimal year. For example, should 2001/01/01 00:00:00 be day 1.0 or day 0.0. Following TPM C code, the code below assumes that it is 1.0. So 2000.0 is not 2000/01/01 00:00:00.

In [71]: gcal2gyear(2000,1,1)

Out[71]: 2000.0027322404371

In [72]: gyear2gcal(_)

Out[72]: (1999, 12, 31, 0.9999999999854481)

In [73]: gcal2gyear(2000,1,0)

Out[73]: 2000.0

In [74]: gyear2gcal(_)

Out[74]: (1999, 12, 31, 0.0)

The result above is 2000/01/01 00:00:00 to within ~1^-6 seconds. Another issue to consider before releasing to public!

In addition I am not sure if I want to include them in jdcal at all, since I am trying to keep it just for Julian date calendar date conversion. I was working on a calendar module that can do all types of conversions, but got distracted by other things. The code below is salvaged from that work.

Hope this helps, and let me know if there are any problems.

That’s pretty good. I tried the following, which is offset from your solution by 1 day:

import jdcal

import datetime

import calendar

def decimal_year_to_JD(yr):

“””

Convert a decimal year (e.g. 2007.4523) to a Julian date

“””

days_in_year = calendar.isleap(int(yr))

date = datetime.timedelta(days=jdcal.fpart(yr)*days_in_year) + datetime.datetime(year=jdcal.ipart(yr),month=1,day=1)

jd,mjd = jdcal.gcal2jd(date.year,date.month,date.day)

mjd += (date.hour + date.minute/60. + date.second/3600.)/24.

return mjd

and I’m not entirely clear why; I agree with you that 2000.0 = (2000,1,1) [1999,12,31,0.999999 is close enough]. Problem is, I get this:

In [170]: gyear2gcal(2000)

Out[170]: (1999, 12, 31, 0.0)

Hello,

>> I agree with you that 2000.0 = (2000,1,1) [1999,12,31,0.999999 is close enough]. Problem is, I >> get this:

>> In [170]: gyear2gcal(2000)

>> Out[170]: (1999, 12, 31, 0.0)

No, according to the algorithm I have, year 2000.0 == date (1999,12,31, 00:00:00), and year 2000.0027322404371 == date (2000,1,1, 00:00:00), i.e., 2000.0 + (1/366.0) == (2000,1,1, 00:00:00). So in the convention I use the year starts at 2000.00273… .

I think this explains the fact that the output from your code is +1 day from mine.

What I meant was that I do not know if all astronomy code use the convention that I have adopted. I am using the convention in TPM C library; which as far as I can gather is used at some observatories, and so has most likely been well tested. This is why I was wondering if you have experience with some other code!

One way to test this would be see if you can identify the date used by the author for the original example you mentioned.

Since I had to stop working on the calendar module I was working on, I haven’t had an opportunity to properly explore all conventions. We could ask the astropy mailing list and/or the GalaxyZoo forum.

Ah, alright, knowing the convention is important. Unfortunately, it’s not entirely clear in the example I’m looking at: http://surveys.roe.ac.uk/wsa/www/WSA_TABLE_gpsSourceSchema.html#gpsSource (“epoch”).

Oh, this could be a very different thing: the Julian Epoch of the observation date, and not the calendar date as a decimal year!!! Now you can see why there can be a lot of confusion regarding dates!

The only way to be absolutely sure is to ask them directly. If not, I think it would safe to assume that this is the Julian epoch, and not calendar date as decimal year. There are two epochs: Julian and Besselian. Modern, i.e., on and after 1980.0, values are always supposed to be Julian epochs.

I have updates the Gist with two functions, and a constant, that can calculate the Julian Epoch of a given JD, and vice-versa.

In [3]: jepoch2jd(2007.23467)

Out[3]: (2400000.5, 54186.963217500015)

In [4]: jd2jepoch(*_)

Out[4]: 2007.23467

Comparison with SLALIB:

In [9]: slalib.sla_epj2d(2007.23467)

Out[9]: 54186.963217500015

In [10]: slalib.sla_epj(_)

Out[10]: 2007.23467