Next / Previous / Contents / TCC Help System / NM Tech homepage

13.7. JulianDate.fromDatetime(): Convert a datetime to a Julian date

This static method converts a JulianDate instance JD to one of Python's regular datetime objects. In Duffett-Smith, this is section 4.

This method handles both “naive” and “aware” datetime instances. You may want to review these concepts in the online documentation for datetime. A naive instance is assumed to represent UTC. For an aware instance, we will use its .utcoffset() method to convert it to UTC.

sidereal.py
# - - -   J u l i a n D a t e . f r o m D a t e t i m e

#   @staticmethod
    def fromDatetime ( dt ):
        """Create a JulianDate instance from a datetime.datetime.

          [ dt is a datetime.datetime instance ->
              if  dt is naive ->
                return the equivalent new JulianDate instance,
                assuming dt expresses UTC
              else ->
                return a new JulianDate instance for the UTC
                time equivalent to dt ]              
        """

The first step is to derive the UTC equivalent to dt. The documentation states that the return value from .utcoffset() is one of:

sidereal.py
        #-- 1 --
        # [ if dt is naive ->
        #     utc  :=  dt
        #   else ->
        #     utc  :=  dt - dt.utcoffset() ]
        utc  =  dt
        offset  =  dt.utcoffset()
        if  offset:
            utc  =  dt - offset

We synthesize a fractional day from the hours, minutes, seconds, and microseconds attributes. For the conversion of hours/minutes/seconds to hours, see Section 10.2, “MixedUnits.mixToSingle(): Convert to a single value”; the resulting hours are divided by 24.0 to get days.

sidereal.py
        #-- 2 --
        # [ fracDay  :=  fraction of a day in [0.0,1.0) made from
        #       utc.hour, utc.minute, utc.second, and utc.microsecond ]
        s  =  float(utc.second) + float(utc.microsecond)*1e-6
        hours  =  dmsUnits.mixToSingle ( (utc.hour, utc.minute, s) )
        fracDay  =  hours / 24.0

We extract the year, month, and day from utc. This is Step 1 from the book.

sidereal.py
        #-- 3 --
        y  =  utc.year
        m  =  utc.month
        d  =  utc.day

Step 2: “If m=1 or m=2 subtract 1 from y and add 12 to m. Otherwise y'=y and m'=m.” Rather than having separate variables for y' and m', we'll just modify variables y and d in place.

sidereal.py
        #-- 4 --
        if  m <= 2:
            y, m  =  y-1, m+12

Step 3: “If the date is later than 1582 October 15 (i.e., Gregorian calendar) calculate:

Otherwise B = 0.” Note how easy it is to compare dates in mixed units: Python defines ordering on tuples in the correct way.

sidereal.py
        #-- 5 --
        if  ( (y, m, d) >= (1582, 10, 15) ):
            A  =  int ( y / 100 )
            B  =  2 - A + int ( A / 4 )
        else:
            B  =  0

Step 4: “Calculate C = integer part of (365.25×y').” Step 5: “Calculate D = integer part of (30.6001×(m'+1)).”

sidereal.py
        #-- 6 --
        C  =  int ( 365.25 * y )
        D  =  int ( 30.6001 * ( m + 1 ) )

Step 6: “Find JD = B + C + D + d + 1 720 994.5. This is the Julian date.” To preserve extra precision over a purely float representation of the Julian date, we will do the addition of the last constant in multiple steps. First we'll add the 0.5 fractional part to fracDay and, if that sum exceeds 1.0, add the carry to the day number d, retaining the fractional part in fracDay. Then we'll add the remaining parts together to get the whole part.

sidereal.py
        #-- 7 --
        # [ if fracDay+0.5 >= 1.0 ->
        #     s  +=  1
        #     fracDay  :=  (fracDay+0.5) % 1.0
        #   else ->
        #     fracDay  :=  fracDay + 0.5 ]
        dayCarry, fracDay  =  divmod ( fracDay+0.5, 1.0 )
        d  +=  dayCarry

        #-- 8 --
        j  =  B + C + D + d + 1720994

        #-- 9 --
        return  JulianDate ( j, fracDay )

This next line is necessary to designate this method as a static method.

sidereal.py
    fromDatetime = staticmethod(fromDatetime)