Форматирование объекта datetime python в качестве аргумента

Несколько новичок в python, но пытаюсь использовать его для анализа спутниковой орбиты. Я действительно не получаю объект и методы datetime. Я хочу иметь возможность передавать объект datetime функции, которая принимает аргументы в том же формате, что и datetime (год, понедельник, день, час, мин, сек). Приведенный ниже код работает, но должен быть лучший способ. Пожалуйста, просветите меня. Спасибо!

import jday
import datetime

jdate = jday.JD(2015,12,1,22,8,0) # example

now1 = datetime.datetime.now().strftime("%Y,%m,%d,%H,%M,%S")
now2 = now1.split(",")
now3 = [int(i) for i in now2]
jdatenow = jday.JD(*now3)
print jdatenow

Модуль jday перенесен из исходного кода Matlab of Astrodynamics Дэвида Вальядо.

import math as m

def JD(yr, mon, day, hr, min, sec):
        jd = 367.0 * yr - m.floor( 
        (7 * (yr + m.floor( (mon + 9) / 12.0) ) ) * 0.25 ) + m.floor(
        275 * mon / 9.0 ) + day + 1721013.5 + (
        (sec/60.0 + min ) / 60.0 + hr ) / 24.0
        return jd

person Jesse Reich    schedule 02.12.2015    source источник
comment
будет ли встроенная версия лучше для вас? нравится now=[int(i) for i in datetime.datetime.now().strftime("%Y,%m,%d,%H,%M,%S").split(",")]   -  person Antonio Ragagnin    schedule 04.12.2015
comment
несвязанный: формула неверна на целый день в 19:00 и 21:00, см. подробности в моем ответе   -  person jfs    schedule 09.12.2015


Ответы (4)


Учитывая, что вы перенесли код JD и поэтому можете управлять им как модулем jday, возможно, вам нужен декоратор. Это имеет очевидное преимущество, поскольку не нарушает исходную подпись функции (для существующего клиентского кода), но добавляет удобство параметра даты, как вы просили.

Также был создан модуль jday2, который аналогичен исходному модулю jday, но чья функция JD() вместо этого напрямую принимает объект даты. Это самое простое решение, если вам не нужна обратная совместимость.

См. рабочий пример кода ниже:

jday.py

import math as m
import functools

def date_support_wrapper(f):
    """ Wraps JD and provides a way to pass a date param

    :param f: the original function
    :return: the wrapper around the original function
    """

    @functools.wraps(f)
    def wrap(*args, **kwargs):
        if 'date' in kwargs:
            d = kwargs['date']
            return f(yr=d.year, mon=d.month, day=d.day, hr=d.hour, min=d.minute, sec=d.second)

        return f(*args, **kwargs)

    return wrap


@date_support_wrapper
def JD(yr, mon, day, hr, min, sec):
    jd = 367.0 * yr - m.floor(
        (7 * (yr + m.floor((mon + 9) / 12.0))) * 0.25) + m.floor(
        275 * mon / 9.0) + day + 1721013.5 + (
                                                 (sec / 60.0 + min) / 60.0 + hr) / 24.0
    return jd

jday2.py

import math as m

def JD(dt):
    """ Same calculation as JD but accepts a date object argument

    :param dt: the date object
    :return: the JD result
    """
    yr, mon, day, hr, min, sec = dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second
    jd = 367.0 * yr - m.floor(
        (7 * (yr + m.floor((mon + 9) / 12.0))) * 0.25) + m.floor(
        275 * mon / 9.0) + day + 1721013.5 + (
                                                 (sec / 60.0 + min) / 60.0 + hr) / 24.0
    return jd

И пример кода клиента:

client.py

import datetime
import jday
import jday2

# The date we are interested in
a = dict(year=2015, month=12, day=1, hour=22, minute=8, second=0)
dt = datetime.datetime(**a)  # 2015-12-01 22:08:00

# The original signature of the function
jdate1 = jday.JD(a['year'], a["month"], a["day"], a["hour"], a["minute"], a["second"])
# 2457358.422222222

# The new signature that accepts a normal date object
# Note that we use keyword "date" argument
jdate2 = jday.JD(date=dt)
# 2457358.422222222

# The new signature that accepts a normal date object
jdate3 = jday2.JD(dt)
# 2457358.422222222
person arcseldon    schedule 02.12.2015
comment
Мне нужно будет больше прочитать о концепции декоратора, но я думаю, что это то, что я надеялся сделать. Измените функцию jday, чтобы она принимала объект даты. Спасибо. - person Jesse Reich; 03.12.2015
comment
@JesseReich - пожалуйста, отметьте это как принятый ответ :) Декораторы - отличный способ использования аспектно-ориентированного программирования (АОП) для добавления / изменения существующего поведения данной функции без фактического изменения самой этой функции. Это просто зависит от того, что вам нужно - если у вас нет причин поддерживать оба типа аргументов функции, просто измените существующую подпись JD, чтобы вместо этого принять объект даты, и проанализируйте аргументы, которые вы хотите, из этого объекта даты. - person arcseldon; 03.12.2015
comment
@JesseReich - только что обновил ответ (в конце концов ..), включив в него отдельный модуль jday2.py, который может быть всем, что вам нужно, если вы считаете, что декораторы слишком много для ваших нужд. - person arcseldon; 03.12.2015

К сожалению, вы не можете передать объект datetime напрямую функции, которая ожидает целые числа. Есть ли какая-то особая причина, по которой вы не можете вызвать функцию с полями, которые предоставляет datetime?

now = datetime.now()
jdatenow = jday.JD(now.year, now.month, now.day, now.hour, now.minute, now.second)

Или, если это слишком утомительно, завершите это:

from datetime import datetime

def my_JD(dt):
    return jday.JD(dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)
jdatenow = my_JD(datetime.now())

Это может быть легче понять, чем

jdatenow = jday.JD(*list(datetime.now().timetuple())[:-3])
person Aaron B    schedule 02.12.2015
comment
Хороший звонок, определенно недостаточно знал о python или datetime, чтобы использовать эти поля таким образом. Спасибо. - person Jesse Reich; 03.12.2015

Вы можете переписать его без math.floor():

def jdate(year, month, day, hour, minute, second):
    day_fraction = ((second + 60 * minute) + 3600 * hour) / 86400.
    return (367 * year - (7 * (year + (month + 9) // 12)) // 4 +
            275 * month // 9 + day + (1721013.5 + day_fraction))

Вы можете упростить его; если вы используете datetime арифметику:

#!/usr/bin/env python3
from datetime import datetime, timedelta

DAY = timedelta(1)
JULIAN_EPOCH = datetime(2000, 1, 1, 12)  # noon (the epoch name is unrelated)
J2000_JD = timedelta(2451545)  # julian epoch in julian dates

def JD(dt):
    """Julian Date: JD(UTC)."""
    return (dt - JULIAN_EPOCH + J2000_JD) / DAY

Чтобы передать объект datetime в jdate(), вы можете использовать метод .timetuple():

import math

for time_tuple in [(1961, 1, 1), (1968, 2, 1), (1972, 1, 1), (1996, 1, 1)]:
    dt = datetime(*time_tuple)
    a, b = jdate(*dt.timetuple()[: 6]), JD(dt)
    print("{} UTC -> {} JD(UTC)".format(dt, b))
    assert math.isclose(a, b), (a, b)

Кроме того, при необходимости вы можете использовать атрибуты dt.year, dt.month, dt.day и т. д.

Выход

1961-01-01 00:00:00 UTC -> 2437300.5 JD(UTC)
1968-02-01 00:00:00 UTC -> 2439887.5 JD(UTC)
1972-01-01 00:00:00 UTC -> 2441317.5 JD(UTC)
1996-01-01 00:00:00 UTC -> 2450083.5 JD(UTC)

Это верно согласно веб-сайту IERS, где рекомендуется Дата" предоставляется.


Формулы дают разные результаты для дат до марта 1900 г. и после февраля 2100 г.:

import jdcal  # pip install jdcal
import astropy.time  # pip install astropy

print("                UTC |    matlab |  datetime |   astropy | jdcal")
for year in [1900, 2000, 2100]:
    for time_tuple in [(year, 2, 28, 12), (year, 3, 1, 12)]:
        dt = datetime(*time_tuple)
        matlabJD = jdate(*dt.timetuple()[:6])
        datetimeJD = JD(dt)
        jdcalJD = sum(jdcal.gcal2jd(*dt.timetuple()[:3])) + .5
        astropyJD = astropy.time.Time(dt)
        print("{dt} | {matlabJD} | {datetimeJD} | {astropyJD.jd} | {jdcalJD}"
              .format(**vars()))

Выход

                UTC |    matlab |  datetime |   astropy | jdcal
1900-02-28 12:00:00 | 2415078.0 | 2415079.0 | 2415079.0 | 2415079.0
1900-03-01 12:00:00 | 2415080.0 | 2415080.0 | 2415080.0 | 2415080.0
2000-02-28 12:00:00 | 2451603.0 | 2451603.0 | 2451603.0 | 2451603.0
2000-03-01 12:00:00 | 2451605.0 | 2451605.0 | 2451605.0 | 2451605.0
2100-02-28 12:00:00 | 2488128.0 | 2488128.0 | 2488128.0 | 2488128.0
2100-03-01 12:00:00 | 2488130.0 | 2488129.0 | 2488129.0 | 2488129.0

Формула jdate() в вашем вопросе считает, что 1900, 2100 високосные годы. Реализация datetime, библиотеки astropy и jdcal дают здесь одинаковые результаты.

Примечание. день по юлианскому календарю – это целое число. JD() вычисляет юлианскую дату, которая включает часть дня, см. определения в ссылках.

Как упоминалось в связанном обсуждении, вы должны использовать уже созданные библиотеки и отправлять исправления, если это необходимо, вместо того, чтобы изобретать колесо, чтобы избежать простых ошибок из-за високосных лет, проблем с плавающей запятой, неправильных шкал времени, небольшая разница в определениях термина Юлианский день и др.

person jfs    schedule 04.12.2015
comment
В спутниковом орбитальном анализе могут быть важны доли секунды, и существует несколько временных масштабов, которые могут быть выбраны в зависимости от деталей расчета. Выход любого решения, которое в конечном итоге будет выбрано, должен правильно обозначать временную шкалу. Простая метка UTC вызовет подозрение у тех, кто получает выходные данные, поскольку различные программные библиотеки, которые пытаются поддерживать юлианские даты, по-разному обрабатывают дополнительные секунды. - person Gerard Ashton; 05.12.2015
comment
@GerardAshton: UTC означает UTC в моем ответе. Щелкните ссылку с IERS в ответе. - person jfs; 05.12.2015
comment
АДж.Ф. Себастьян: [Источник IAU] iausofa.org/sofa_ts_c.pdf указывает, что UTC был инициирован в 1960 году. поэтому выведите дату/время маркировки в 1900 году, поскольку UTC выглядит подозрительно. - person Gerard Ashton; 05.12.2015
comment
@GerardAshton: цель 1900 — сравнить различные реализации: разница составляет целый день, что намного больше любой разницы между временными шкалами (такими как UT1, TAI) . - person jfs; 05.12.2015
comment
@GerardAshton: если вам нужна точность до доли секунды, вам, вероятно, следует использовать единую шкалу времени (например, GPS), как рекомендовано в ссылке IERS. UTC неравномерно (из-за дополнительных секунд). - person jfs; 05.12.2015

Самый короткий способ, который я мог понять:

now = list(datetime.datetime.now().timetuple())[:-3]

person Alex    schedule 02.12.2015