在 Python 中将 datetime.timedelta 转换为 ISO 8601 持续时间?

问题描述 投票:0回答:4

给定一个

datetime.timedelta
,如何将其转换为 ISO 8601 持续时间字符串?

例如,给定

datetime.timedelta(0, 18, 179651)
,我如何得到
'PT18.179651S'


相反,请参阅是否有一种简单的方法可以将 ISO 8601 持续时间转换为 timedelta?

python iso8601 timedelta python-datetime
4个回答
12
投票

尽管

datetime
模块 包含
datetime
date
对象的 ISO 8601 表示法的实现,但目前(Python 3.7)不支持
timedelta
对象的相同实现。但是,
isodate
模块(pypi链接)
具有以ISO 8601表示法生成持续时间字符串的功能:

In [15]: import isodate, datetime

In [16]: print(isodate.duration_isoformat(datetime.datetime.now() - datetime.datetime(1985, 8, 13, 15)))
P12148DT4H20M39.47017S

这意味着12148天4小时20分钟39.47017秒。


5
投票

这是来自 Tin Can Python 项目(Apache License 2.0)的函数,可以进行转换:

def iso8601(value):
    # split seconds to larger units
    seconds = value.total_seconds()
    minutes, seconds = divmod(seconds, 60)
    hours, minutes = divmod(minutes, 60)
    days, hours = divmod(hours, 24)
    days, hours, minutes = map(int, (days, hours, minutes))
    seconds = round(seconds, 6)

    ## build date
    date = ''
    if days:
        date = '%sD' % days

    ## build time
    time = u'T'
    # hours
    bigger_exists = date or hours
    if bigger_exists:
        time += '{:02}H'.format(hours)
    # minutes
    bigger_exists = bigger_exists or minutes
    if bigger_exists:
      time += '{:02}M'.format(minutes)
    # seconds
    if seconds.is_integer():
        seconds = '{:02}'.format(int(seconds))
    else:
        # 9 chars long w/leading 0, 6 digits after decimal
        seconds = '%09.6f' % seconds
    # remove trailing zeros
    seconds = seconds.rstrip('0')
    time += '{}S'.format(seconds)
    return u'P' + date + time

例如

>>> iso8601(datetime.timedelta(0, 18, 179651))
'PT18.179651S'

1
投票

我尝试编写一个更短的函数:

def iso8601(tdelta):
    ts = tdelta.total_seconds()
    d = int(ts // 86400)
    s = round(ts % 60, 6)
    hms = int(ts // 3600 % 24), int(ts // 60 % 60), s if s % 1 != 0 else int(s)
    t = ''.join([str(p[0]) + p[1] for p in zip(hms, ['H', 'M', 'S']) if p[0]])
    sep = 'T' if any(hms) else ''
    return 'P' + (str(d) + 'D' if d else '') + sep + (t if ts else 'T0S')

例如

>>> iso8601(datetime.timedelta(0, 18, 179651))
'PT18.179651S'

这对于某些人来说可能不太可读,但我认为在某些用例中将其拆开并内联会更容易。例如,如果您没有使用 timedelta,但已经具有以整数秒为单位的持续时间

ts
,并且您的持续时间总是短于一天,则这将变为:

hms = ts // 3600, ts // 60 % 60, ts % 60
t = ''.join([str(p[0]) + p[1] for p in zip(hms, ['H', 'M', 'S']) if p[0]])
ts_iso8601 = 'PT' + t if ts else 'PT0S'

0
投票

如果您正在使用Pydantic

>>> from datetime import timedelta
>>> from pydantic import TypeAdapter
>>> timedelta_adapter = TypeAdapter(timedelta)
>>> dt = timedelta(0, 18, 179651)
>>> timedelta_adapter.dump_python(dt, mode="json")
'PT18.179651S'

对于 ISO 8601 字符串,

datetime.timedelta
请参阅此处

说明

Pydantic 是一个数据验证库。它接受并输出 ISO 8601 字符串 如果声明的类型是

datetime.timedelta
。通过使用
TypeAdapter
,您可以对单一类型而不是 Pydantic 模型进行验证/转储操作。

© www.soinside.com 2019 - 2024. All rights reserved.