Python 中缺少 datetime.timedelta.to_seconds() -> float?

9 投票
2 回答
8563 浏览
提问于 2025-04-15 12:41

我明白在datetime.timedelta中,秒和微秒可能是分开表示的,这样做是为了提高效率。不过,我刚写了一个简单的函数:

def to_seconds_float(timedelta):
    """Calculate floating point representation of combined
    seconds/microseconds attributes in :param:`timedelta`.

    :raise ValueError: If :param:`timedelta.days` is truthy.

        >>> to_seconds_float(datetime.timedelta(seconds=1, milliseconds=500))
        1.5
        >>> too_big = datetime.timedelta(days=1, seconds=12)
        >>> to_seconds_float(too_big) # doctest: +ELLIPSIS
        Traceback (most recent call last):
        ...
        ValueError: ('Must not have days', datetime.timedelta(1, 12))
    """
    if timedelta.days:
        raise ValueError('Must not have days', timedelta)
    return timedelta.seconds + timedelta.microseconds / 1E6

这个函数在一些场合很有用,比如传值给time.sleep或者select.select但为什么像这样的功能不直接包含在datetime.timedelta的接口里呢?我可能漏掉了一些特殊情况。时间的表示方式似乎有很多不明显的特殊情况...

我直接把天数排除了,这样可以更好地保持一些精度(我现在懒得去算具体的数学,所以这样做看起来是个合理的折中方案;-)。

2 个回答

3

你对精确度的担心其实没必要。这里有一个简单的两行代码,可以大致计算一下在IEEE754 64位浮点数剩下的53位精度中,你能挤出多少年:

>>> import math
>>> 10 ** (math.log10(2 ** 53) - math.log10(60 * 60 * 24) - 6) / 365.25
285.42092094268787
>>>

注意要小心舍入误差;先加上最小的非零数字:

return timedelta.seconds + timedelta.microseconds / 1E6 + timedelta.days * 86400
12

Python中的浮点数大约有15位有效数字,所以在计算秒数时,最多可以到86400(小数点左边有5位数字),而微秒需要6位数字,这样你可以轻松地包括天数(甚至可以算上好几年的时间),而不会失去精度。

一个好记的口号是“π秒是一个纳秒世纪”——也就是每100年大约有3.14亿秒,换算成每年大约是3000万秒,每年则有3E13微秒。这个口号好记,因为它容易记住,虽然之后你可能需要做一点简单的数学运算(不过,就像菠菜一样,这对你有好处——能让你保持灵活和警觉!)。

datetime的设计理念有点简约,所以它省略了很多可能的辅助方法,这些方法其实都可以用简单的数学表达式来解决。

撰写回答