Erik Grinaker avatar Erik Grinaker committed 549aa58

added support for julian days

Comments (0)

Files changed (11)

 New features:
 
 * Added Date.delta() and DateTime.delta() for difference calculations
+* Added support for julian day numbers
 
 2010-02-06: 0.2.0
 =================

chrono/calendar/calendar.py

 #
 
 from __future__ import absolute_import
+from __future__ import division
 
 from .. import error
 from .. import utility
             return year
 
     @classmethod
+    def julian(cls, year, month, day):
+        """
+        Converts a date to a julian day number.
+
+        Raises :exc:`chrono.error.YearError`, :exc:`chrono.error.MonthError` or
+        :exc:`chrono.error.DayError` if *year*, *month* or *day* is invalid.
+        """
+
+        year = utility.int_year(year)
+        month = utility.int_month(month)
+        day = utility.int_day(day)
+
+        cls.validate(year, month, day)
+
+        a = (14 - month) // 12
+        y = year + 4800 - a
+        m = month + (12 * a) - 3
+        p = day + (((153 * m) + 2) // 5) + (365 * y)
+        q = (y // 4) - (y // 100) + (y // 400) - 32045
+
+        return p + q
+
+    @classmethod
+    def julian_to_date(cls, julian):
+        """
+        Converts a julian day number to a date, returns a tuple of year,
+        month, and day.
+
+        Raises :exc:`chrono.error.DayError` if *julian* is invalid.
+        """
+
+        try:
+            julian = int(julian)
+
+        except ValueError:
+            raise error.DayError("Invalid julian day '{0}'".format(julian))
+
+        j = julian + 32044
+        g = j // 146097
+        dg = j % 146097
+        c = (dg // 36524 + 1) * 3 // 4
+        dc = dg - c * 36524
+        b = dc // 1461
+        db = dc % 1461
+        a = (db // 365 + 1) * 3 // 4
+        da = db - a * 365
+        y = g * 400 + c * 100 + b * 4 + a
+        m = (da * 5 + 308) // 153 - 2
+        d = da - (m + 4) * 153 // 5 + 122
+
+        year = int(y - 4800 + (m + 2) // 12)
+        month = int((m + 2) % 12 + 1)
+        day = int(d + 1)
+
+        return (year, month, day)
+
+    @classmethod
     def leapyear(cls, year):
         """
         Returns **True** if *year* is a leap year, otherwise **False**.

chrono/clock/clock.py

 #
 
 from __future__ import absolute_import
+from __future__ import division
 
 from .. import error
 from .. import utility
 
 import calendar
 import datetime
+import math
 
 
 class Clock(object):
     """
 
     @classmethod
+    def julian(cls, hour, minute, second):
+        """
+        Returns the julian time for the given time, as a float between
+        0.0 and 1,0.
+
+        Raises :exc:`chrono.error.HourError`, :exc:`chrono.error.MinuteError`,
+        or :exc:`chrono.error.SecondError` if *hour*, *minute*, or *second*
+        is invalid.
+        """
+
+        hour = utility.int_hour(hour)
+        minute = utility.int_minute(minute)
+        second = utility.int_second(second)
+
+        cls.validate(hour, minute, second)
+
+        seconds = hour * 60 * 60 + minute * 60 + second
+
+        return seconds / 86400
+
+    @classmethod
+    def julian_to_time(cls, julian):
+        """
+        Converts a julian time as a float between 0 and 1 to a tuple of hour,
+        minute, and second. For values > 0, only the decimal part is used.
+
+        Raises :exc:`chrono.error.TimeError` on invalid input.
+        """
+
+        try:
+            julian = float(julian)
+
+        except ValueError:
+            raise error.TimeError("Invalid julian time '{0}'".format(julian))
+
+        julian = julian - math.floor(julian)
+
+        julian *= 86400
+
+        hour = math.floor(julian / 60 / 60)
+        minute = math.floor(julian / 60 % 60)
+        second = math.floor(julian % 60)
+
+        return (hour, minute, second)
+
+    @classmethod
     def validate(cls, hour, minute, second):
         """
         Validates a time: *hour* must be in range 0-23, *minute* in range
 
         return datetime.date(self.year, self.month, self.day)
 
+    def get_julian(self):
+        """
+        Returns the julian day number for the date.
+
+        Raises :exc:`chrono.error.NoDateTimeError` on missing date data.
+        """
+
+        self.assert_set()
+
+        return self.calendar.julian(self.year, self.month, self.day)
+
     def get_string(self):
         """
         Returns a string representation (*yyyy-mm-dd*) of the date.
 
         self.set(datetime.year, datetime.month, datetime.day)
 
+    def set_julian(self, julian):
+        """
+        Sets the date from a julian day number.
+
+        Raises :exc:`chrono.error.DayError` on invalid julian day.
+        """
+
+        y, m, d = self.calendar.julian_to_date(julian)
+
+        self.set(y, m, d)
+
     def set_now(self):
         """
         Sets the date to the current date.

chrono/datetime.py

             self.hour, self.minute, self.second
         )
 
+    def get_julian(self):
+        """
+        Returns the julian day number for the datetime.
+
+        Raises :exc:`chrono.error.NoDateTimeError` on missing date data.
+        """
+
+        self.assert_set()
+
+        return date.Date.get_julian(self) + time.Time.get_julian(self)
+
     def get_string(self):
         """
         Returns a string representation (*yyyy-mm-dd hh:mm:ss*) of the
             datetime.hour, datetime.minute, datetime.second
         )
 
+    def set_julian(self, julian):
+        """
+        Sets the datetime from a julian day number.
+
+        Raises :exc:`chrono.error.DayError` or
+        :exc:`chrono.error.TimeError` on invalid julian day.
+        """
+
+        year, month, day = self.calendar.julian_to_date(julian)
+        hour, minute, second = clock.Clock.julian_to_time(julian)
+
+        self.set(year, month, day, hour, minute, second)
+
+
     def set_now(self):
         """
         Sets the datetime to the current date and time.
 
         return datetime.time(self.hour, self.minute, self.second)
 
+    def get_julian(self):
+        """
+        Returns a julian time for the set time, as a float between
+        0 and 1.
+
+        Raises :exc:`chrono.error.NoDateTimeError` on missing time data.
+        """
+
+        self.assert_set()
+
+        return clock.Clock.julian(self.hour, self.minute, self.second)
+
     def get_string(self):
         """
         Returns a string represenation (*hh:mm:ss*) of the time.
 
         self.set(datetime.hour, datetime.minute, datetime.second)
 
+    def set_julian(self, julian):
+        """
+        Sets the time from a julian time, as a float between 0 and 1.
+        If *julian* is greather than 1, only the decimal part will be
+        used.
+
+        Raises :exc:`chrono.error.TimeError` on invalid julian time.
+        """
+
+        h, m, s = clock.Clock.julian_to_time(julian)
+
+        self.set(h, m, s)
+
     def set_now(self):
         """
         Sets the time to the current time.

tests/test_calendar/test_calendar.py

         self.assertEqual(chrono.calendar.Calendar.fullyear("29"), 2029)
 
 
+class Calendar_julianTest(unittest.TestCase):
+
+    def test_1858_11_16(self):
+        "Calendar.julian() returns 2400000 for 1858-11-16"
+
+        self.assertEquals(
+            2400000, chrono.calendar.Calendar.julian(1858, 11, 16)
+        )
+
+    def test_2007_01_14(self):
+        "Calendar.julian() returns 2454115 for 2007-01-14"
+
+        self.assertEquals(
+            2454115, chrono.calendar.Calendar.julian(2007, 1, 14)
+        )
+
+    def test_2010_02_14(self):
+        "Calendar.julian() returns 2455242 for 2010-02-14"
+
+        self.assertEquals(
+            2455242, chrono.calendar.Calendar.julian(2010, 2, 14)
+        )
+
+    def test_2132_08_31(self):
+        "Calendar.julian() returns 2500000 for 2132-08-31"
+
+        self.assertEquals(
+            2500000, chrono.calendar.Calendar.julian(2132, 8, 31)
+        )
+
+    def test_invalid(self):
+        "Calendar.julian() raises proper error on invalid input"
+
+        self.assertRaises(
+            chrono.YearError, chrono.calendar.Calendar.julian, 10000, 7, 23
+        )
+        self.assertRaises(
+            chrono.MonthError, chrono.calendar.Calendar.julian, 2010, 13, 23
+        )
+        self.assertRaises(
+            chrono.DayError, chrono.calendar.Calendar.julian, 2010, 7, 32
+        )
+
+    def test_string(self):
+        "Calendar.julian() accepts string input"
+
+        self.assertEquals(
+            2455242, chrono.calendar.Calendar.julian("2010", "2", "14")
+        )
+
+
+class Calendar_julian_to_dateTest(unittest.TestCase):
+
+    def test_1858_11_16(self):
+        "Calendar.julian_to_date() returns 1858-11-16 2400000"
+
+        self.assertEquals(
+            (1858, 11, 16), chrono.calendar.Calendar.julian_to_date(2400000)
+        )
+
+    def test_2007_01_14(self):
+        "Calendar.julian_to_date() returns 2007-01-14 for 2454115"
+
+        self.assertEquals(
+            (2007, 1, 14), chrono.calendar.Calendar.julian_to_date(2454115)
+        )
+
+    def test_2010_02_14(self):
+        "Calendar.julian_to_date() returns 2010-02-14 for 2455242"
+
+        self.assertEquals(
+            (2010, 2, 14), chrono.calendar.Calendar.julian_to_date(2455242)
+        )
+
+    def test_2132_08_31(self):
+        "Calendar.julian_to_date() returns 2132-08-31 for 2500000"
+
+        self.assertEquals(
+            (2132, 8, 31), chrono.calendar.Calendar.julian_to_date(2500000)
+        )
+
+    def test_invalid(self):
+        "Calendar.julian_to_date() raises DayError on invalid input"
+
+        self.assertRaises(
+            chrono.DayError, chrono.calendar.Calendar.julian_to_date, "xyz"
+        )
+
+    def test_string(self):
+        "Calendar.julian_to_date() accepts string input"
+
+        self.assertEquals(
+            (2010, 2, 14), chrono.calendar.Calendar.julian_to_date(2455242)
+        )
+
+
 class Calendar_leapyearTest(unittest.TestCase):
 
     def test_invalid(self):

tests/test_clock/test_clock.py

 import unittest
 
 
+class Clock_julianTest(unittest.TestCase):
+
+    def test_0_0_0(self):
+        "Clock.julian() returns 0 for 00:00:00"
+
+        self.assertEquals(chrono.clock.Clock.julian(0, 0, 0), 0)
+
+    def test_12_0_0(self):
+        "Clock.julian() returns 0.5 for 12:00:00"
+
+        self.assertEquals(chrono.clock.Clock.julian(12, 0, 0), 0.5)
+
+    def test_23_59_59(self):
+        "Clock.julian() returns 0.999988 for 23:59:59"
+
+        self.assertEquals(round(chrono.clock.Clock.julian(23, 59, 59), 6), 0.999988)
+
+    def test_invalid(self):
+        "Clock.julian() raises proper error on invalid input"
+
+        self.assertRaises(
+            chrono.HourError, chrono.clock.Clock.julian, 24, 0, 0
+        )
+        self.assertRaises(
+            chrono.MinuteError, chrono.clock.Clock.julian, 0, 60, 0
+        )
+        self.assertRaises(
+            chrono.SecondError, chrono.clock.Clock.julian, 0, 0, 60
+        )
+
+    def test_string(self):
+        "Clock.julian() accepts strings"
+
+        self.assertEquals(chrono.clock.Clock.julian("12", "0", "0"), 0.5)
+
+
+class Clock_julian_to_timeTest(unittest.TestCase):
+
+    def test_0(self):
+        "Clock.julian_to_time() returns 00:00:00 for 0"
+
+        self.assertEquals(chrono.clock.Clock.julian_to_time(0), (0, 0, 0))
+
+    def test_0_5(self):
+        "Clock.julian_to_time() returns 12:00:00 for 0.5"
+
+        self.assertEquals(chrono.clock.Clock.julian_to_time(0.5), (12, 0, 0))
+
+    def test_0_5486(self):
+        "Clock.julian_to_time() returns 13:09:59 for 0.5486"
+
+        self.assertEquals(
+            chrono.clock.Clock.julian_to_time(0.5486), (13, 9, 59)
+        )
+
+    def test_0_999988(self):
+        "Clock.julian_to_time() returns 23:59:59 for 0.999999"
+
+        self.assertEquals(
+            chrono.clock.Clock.julian_to_time(0.999999), (23, 59, 59)
+        )
+
+    def test_invalid(self):
+        "Clock.juian_to_time() returns TimeError on invalid input"
+
+        self.assertRaises(
+            chrono.TimeError, chrono.clock.Clock.julian_to_time, "abc"
+        )
+
+
 class Clock_validateTest(unittest.TestCase):
 
     def test_hour(self):

tests/test_date.py

         )
 
 
+class Date_get_julianTest(unittest.TestCase):
+
+    def test_empty(self):
+        "Date.get_julian() raises NoDateTimeError if date isn't set"
+
+        self.assertRaises(
+            chrono.error.NoDateTimeError, chrono.Date().get_julian
+        )
+
+    def test_julian(self):
+        "Date.get_julian() returns julian day number"
+
+        self.assertEquals(2455242, chrono.Date("2010-02-14").get_julian())
+
+
 class Date_get_stringTest(unittest.TestCase):
 
     def test_empty(self):
         self.assertEquals(d.get(), (2009, 12, 27))
 
 
+class Date_set_julianTest(unittest.TestCase):
+
+    def test_set(self):
+        "Date.set_julian() sets date from julian day"
+
+        d = chrono.Date()
+        d.set_julian(2455242)
+
+        self.assertEquals(d.get(), (2010, 2, 14))
+
+
 class Date_set_nowTest(unittest.TestCase):
 
     def test_now(self):

tests/test_datetime.py

         )
 
 
+class DateTime_get_julianTest(unittest.TestCase):
+
+    def test_empty(self):
+        "Time.get_julian() raises NoDateTimeError if time is not set"
+
+        self.assertRaises(
+            chrono.error.NoDateTimeError, chrono.DateTime().get_julian
+        )
+
+    def test_get(self):
+        "DateTime.get_julian() returns julian day"
+
+        self.assertEquals(
+            chrono.DateTime("2010-07-23 16:27:43").get_julian(),
+            2455401.6859143518518519
+        )
+
+
 class DateTime_get_stringTest(unittest.TestCase):
 
     def test_empty(self):
         self.assertEquals(d.get(), (2009, 12, 27, 16, 27, 43))
 
 
+class DateTime_set_julianTest(unittest.TestCase):
+
+    def test_set(self):
+        "DateTime.set_julian() sets datetime from julian day"
+
+        dt = chrono.DateTime()
+        dt.set_julian(2455401.6859143518518519)
+
+        self.assertEquals(dt.get(), (2010, 7, 23, 16, 27, 43))
+
+
 class DateTime_set_nowTest(unittest.TestCase):
 
     def test_now(self):

tests/test_time.py

         self.assertEquals(dt.second, 43)
 
 
+class Time_get_julianTest(unittest.TestCase):
+
+    def test_empty(self):
+        "Time.get_julian() raises NoDateTimeError if time is not set"
+
+        self.assertRaises(
+            chrono.error.NoDateTimeError, chrono.Time().get_julian
+        )
+
+    def test_get(self):
+        "Time.get_julian() returns julian time"
+
+        self.assertEquals(
+            chrono.Time("16:27:43").get_julian(), 0.6859143518518519
+        )
+
+
 class Time_get_stringTest(unittest.TestCase):
 
     def test_empty(self):
         self.assertEquals(t.get(), (16, 27, 43))
 
 
+class Time_set_julianTest(unittest.TestCase):
+
+    def test_julian(self):
+        "Time.set_julian() sets time from julian time"
+
+        t = chrono.Time()
+        t.set_julian(0.6859143518518519),
+
+        self.assertEquals(t.get(), (16, 27, 43))
+
+
 class Time_set_nowTest(unittest.TestCase):
 
     def test_now(self):
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.