【问题标题】:Formatting python datetime object as argument将 python 日期时间对象格式化为参数
【发布时间】:2015-12-02 04:17:10
【问题描述】:

对 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 模块是从 David Vallado 的 Astrodynamics 源代码的 Matlab 移植过来的。

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

【问题讨论】:

  • 内联版本会更适合您吗?喜欢now=[int(i) for i in datetime.datetime.now().strftime("%Y,%m,%d,%H,%M,%S").split(",")]
  • 无关:1900年和2100年公式错了一整天,见details in my answer

标签: python datetime satellite julian-date


【解决方案1】:

鉴于您已经移植了 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

【讨论】:

  • 我将不得不阅读有关装饰器概念的更多信息,但我认为这是我希望做的。更改 jday 函数以接受日期对象。谢谢。
  • @JesseReich - 请您将其标记为已接受的答案 :) 装饰器是使用“面向方面编程”(AOP)添加/修改给定函数的现有行为而不实际更改的好方法该功能本身。这仅取决于您需要什么 - 如果您没有理由同时支持这两种函数参数类型,那么只需修改现有的 JD 签名以改为接受日期对象,然后从该日期对象中解析出您想要的参数。
  • @JesseReich - 刚刚更新了答案(最终......)以包含一个单独的 jday2.py 模块,如果您认为装饰器无法满足您的需求,它可能就是您所需要的。
【解决方案2】:

不幸的是,您不能将日期时间对象直接传递给需要整数的函数。您是否有任何特殊原因无法使用 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])

【讨论】:

  • 好电话,肯定对 python 或 datetime 了解不够,无法以这种方式使用这些字段。谢谢。
【解决方案3】:

你可以在没有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 web-site where a recommended "Julian Date" definition is provided,这是正确的。


对于 1900 年 3 月之前和 2100 年 2 月之后的日期,这些公式会产生不同的结果:

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 库在这里产生相同的结果。

注意:Julian day 是一个整数。 JD() 计算一个 Julian 日期,其中包括当天的分数,请参阅 definitions in the links。

正如链接讨论中提到的,您应该使用已经制作的库并在必要时发送补丁而不是重新发明轮子,以避免由于闰年、浮点问题、错误的时间尺度、定义中的细微差别而导致的简单错误用于朱利安日等术语。

【讨论】:

  • 在卫星轨道分析中,几分之一秒可能很重要,并且可以根据计算的细节选择几个时间尺度。最终选择的任何解决方案的输出都应正确标记时间尺度。一个简单的“UTC”标签会引起接收输出的人的怀疑,因为各种尝试支持儒略日期的软件库以不同的方式处理闰秒。
  • @GerardAshton:UTC 在我的回答中意味着 UTC。点击答案中带有 IERS 的链接。
  • AJ.F. Sebastian:[来自 IAU 的消息来源]iausofa.org/sofa_ts_c.pdf 表示 UTC 始于 1960 年,因此输出 1900 年的日期/时间,因为 UTC 表面上是可疑的。
  • @GerardAshton:1900 的目的是比较不同的实现:差异是 一整天,任何时间之间的差异都要大得多音阶(如 UT1、TAI)。
  • @GerardAshton:如果您需要亚秒级精度,那么您可能应该使用 IERS 链接中推荐的统一时标(例如 GPS)。 UTC 不一致(由于闰秒)。
【解决方案4】:

我能想到的最短方法:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-01
    • 2014-01-28
    • 2018-02-20
    • 2016-11-14
    • 1970-01-01
    • 2019-10-04
    • 2019-04-27
    • 1970-01-01
    相关资源
    最近更新 更多