Skip to content

Commit 10ad9dd

Browse files
timcerajreback
authored andcommitted
Initial implementation of calculation of astronomical Julian Date.
* Added 'to_julian_date()' to Timestamp * Added 'to_julian_date()' to DatetimeIndex * Added tests for both methods. TST: Stylistic changes to to_julian date tests. API: DatetimeIndex.to_julian_date now returns a Float64Index. TST: Moved to_julian_date tests from pandas/tseries/tests/test_julian_date.py into pandas/tseries/tests/test_timeseries.py DOC: Added mention of to_julian_date functionality to doc/source/release.rst and v0.14.0.txt DOC: Added docstring to Timestamp.to_julian() TST: Change to use PANDAS testing rather than numpy. DOC: release.rst/v0.14.0.txt updates
1 parent 44714bd commit 10ad9dd

File tree

5 files changed

+144
-2
lines changed

5 files changed

+144
-2
lines changed

doc/source/release.rst

+4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ New features
5555

5656
- Hexagonal bin plots from ``DataFrame.plot`` with ``kind='hexbin'`` (:issue:`5478`)
5757
- Added the ``sym_diff`` method to ``Index`` (:issue:`5543`)
58+
- Added ``to_julian_date`` to ``TimeStamp`` and ``DatetimeIndex``. The Julian
59+
Date is used primarily in astronomy and represents the number of days from
60+
noon, January 1, 4713 BC. Because nanoseconds are used to define the time
61+
in pandas the actual range of dates that you can use is 1678 AD to 2262 AD. (:issue:`4041`)
5862

5963
API Changes
6064
~~~~~~~~~~~

doc/source/v0.14.0.txt

+2
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,8 @@ Enhancements
231231

232232
- ``quotechar``, ``doublequote``, and ``escapechar`` can now be specified when
233233
using ``DataFrame.to_csv`` (:issue:`5414`, :issue:`4528`)
234+
- Added a ``to_julian_date`` function to ``TimeStamp`` and ``DatetimeIndex``
235+
to convert to the Julian Date used primarily in astronomy. (:issue:`4041`)
234236

235237
Performance
236238
~~~~~~~~~~~

pandas/tseries/index.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from pandas.core.common import (isnull, _NS_DTYPE, _INT64_DTYPE,
1010
is_list_like,_values_from_object, _maybe_box,
1111
notnull, ABCSeries)
12-
from pandas.core.index import Index, Int64Index, _Identity
12+
from pandas.core.index import Index, Int64Index, _Identity, Float64Index
1313
import pandas.compat as compat
1414
from pandas.compat import u
1515
from pandas.tseries.frequencies import (
@@ -1759,6 +1759,33 @@ def max(self, axis=None):
17591759
max_stamp = self.asi8.max()
17601760
return Timestamp(max_stamp, tz=self.tz)
17611761

1762+
def to_julian_date(self):
1763+
"""
1764+
Convert DatetimeIndex to Float64Index of Julian Dates.
1765+
0 Julian date is noon January 1, 4713 BC.
1766+
http://en.wikipedia.org/wiki/Julian_day
1767+
"""
1768+
1769+
# http://mysite.verizon.net/aesir_research/date/jdalg2.htm
1770+
year = self.year
1771+
month = self.month
1772+
day = self.day
1773+
testarr = month < 3
1774+
year[testarr] -= 1
1775+
month[testarr] += 12
1776+
return Float64Index(day +
1777+
np.fix((153*month - 457)/5) +
1778+
365*year +
1779+
np.floor(year / 4) -
1780+
np.floor(year / 100) +
1781+
np.floor(year / 400) +
1782+
1721118.5 +
1783+
(self.hour +
1784+
self.minute/60.0 +
1785+
self.second/3600.0 +
1786+
self.microsecond/3600.0/1e+6 +
1787+
self.nanosecond/3600.0/1e+9
1788+
)/24.0)
17621789

17631790
def _generate_regular_range(start, end, periods, offset):
17641791
if isinstance(offset, Tick):

pandas/tseries/tests/test_timeseries.py

+86-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from pandas import (Index, Series, TimeSeries, DataFrame,
1515
isnull, date_range, Timestamp, Period, DatetimeIndex,
16-
Int64Index, to_datetime, bdate_range)
16+
Int64Index, to_datetime, bdate_range, Float64Index)
1717

1818
from pandas.core.daterange import DateRange
1919
import pandas.core.datetools as datetools
@@ -3287,6 +3287,91 @@ def test_guess_datetime_format_for_array(self):
32873287
)
32883288
self.assertTrue(format_for_string_of_nans is None)
32893289

3290+
3291+
class TestTimestampToJulianDate(tm.TestCase):
3292+
3293+
def test_compare_1700(self):
3294+
r = Timestamp('1700-06-23').to_julian_date()
3295+
self.assertEqual(r, 2342145.5)
3296+
3297+
def test_compare_2000(self):
3298+
r = Timestamp('2000-04-12').to_julian_date()
3299+
self.assertEqual(r, 2451646.5)
3300+
3301+
def test_compare_2100(self):
3302+
r = Timestamp('2100-08-12').to_julian_date()
3303+
self.assertEqual(r, 2488292.5)
3304+
3305+
def test_compare_hour01(self):
3306+
r = Timestamp('2000-08-12T01:00:00').to_julian_date()
3307+
self.assertEqual(r, 2451768.5416666666666666)
3308+
3309+
def test_compare_hour13(self):
3310+
r = Timestamp('2000-08-12T13:00:00').to_julian_date()
3311+
self.assertEqual(r, 2451769.0416666666666666)
3312+
3313+
3314+
class TestDateTimeIndexToJulianDate(tm.TestCase):
3315+
def test_1700(self):
3316+
r1 = Float64Index([2345897.5,
3317+
2345898.5,
3318+
2345899.5,
3319+
2345900.5,
3320+
2345901.5])
3321+
r2 = date_range(start=Timestamp('1710-10-01'),
3322+
periods=5,
3323+
freq='D').to_julian_date()
3324+
self.assert_(isinstance(r2, Float64Index))
3325+
tm.assert_index_equal(r1, r2)
3326+
3327+
def test_2000(self):
3328+
r1 = Float64Index([2451601.5,
3329+
2451602.5,
3330+
2451603.5,
3331+
2451604.5,
3332+
2451605.5])
3333+
r2 = date_range(start=Timestamp('2000-02-27'),
3334+
periods=5,
3335+
freq='D').to_julian_date()
3336+
self.assert_(isinstance(r2, Float64Index))
3337+
tm.assert_index_equal(r1, r2)
3338+
3339+
def test_hour(self):
3340+
r1 = Float64Index([2451601.5,
3341+
2451601.5416666666666666,
3342+
2451601.5833333333333333,
3343+
2451601.625,
3344+
2451601.6666666666666666])
3345+
r2 = date_range(start=Timestamp('2000-02-27'),
3346+
periods=5,
3347+
freq='H').to_julian_date()
3348+
self.assert_(isinstance(r2, Float64Index))
3349+
tm.assert_index_equal(r1, r2)
3350+
3351+
def test_minute(self):
3352+
r1 = Float64Index([2451601.5,
3353+
2451601.5006944444444444,
3354+
2451601.5013888888888888,
3355+
2451601.5020833333333333,
3356+
2451601.5027777777777777])
3357+
r2 = date_range(start=Timestamp('2000-02-27'),
3358+
periods=5,
3359+
freq='T').to_julian_date()
3360+
self.assert_(isinstance(r2, Float64Index))
3361+
tm.assert_index_equal(r1, r2)
3362+
3363+
def test_second(self):
3364+
r1 = Float64Index([2451601.5,
3365+
2451601.500011574074074,
3366+
2451601.5000231481481481,
3367+
2451601.5000347222222222,
3368+
2451601.5000462962962962])
3369+
r2 = date_range(start=Timestamp('2000-02-27'),
3370+
periods=5,
3371+
freq='S').to_julian_date()
3372+
self.assert_(isinstance(r2, Float64Index))
3373+
tm.assert_index_equal(r1, r2)
3374+
32903375
if __name__ == '__main__':
32913376
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
32923377
exit=False)

pandas/tslib.pyx

+24
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,30 @@ class Timestamp(_Timestamp):
384384
or self.tzinfo is not None
385385
or self.nanosecond != 0)
386386

387+
def to_julian_date(self):
388+
"""
389+
Convert TimeStamp to a Julian Date.
390+
0 Julian date is noon January 1, 4713 BC.
391+
"""
392+
year = self.year
393+
month = self.month
394+
day = self.day
395+
if month <= 2:
396+
year -= 1
397+
month += 12
398+
return (day +
399+
np.fix((153*month - 457)/5) +
400+
365*year +
401+
np.floor(year / 4) -
402+
np.floor(year / 100) +
403+
np.floor(year / 400) +
404+
1721118.5 +
405+
(self.hour +
406+
self.minute/60.0 +
407+
self.second/3600.0 +
408+
self.microsecond/3600.0/1e+6 +
409+
self.nanosecond/3600.0/1e+9
410+
)/24.0)
387411

388412
_nat_strings = set(['NaT','nat','NAT','nan','NaN','NAN'])
389413
class NaTType(_NaT):

0 commit comments

Comments
 (0)