This static method converts a JulianDate
instance
to one of Python's regular JDdatetime
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.
# - - - 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:
None, if the instance is naive.
A datetime.timedelta instance
representing the offset in hours east of London. This
value must be subtracted from a local time to get UTC.
Note that subtracting a timedelta
instance from a datetime instance
yields a new datetime instance.
#-- 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.
#-- 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.
#-- 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.
#-- 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:
(i) A = integer part of (y'/100);
(ii) B = 2 - A + integer part of (A/4).
Otherwise B = 0.” Note how easy it is to compare dates in mixed units: Python defines ordering on tuples in the correct way.
#-- 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)).”
#-- 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.
#-- 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.
fromDatetime = staticmethod(fromDatetime)