格式化python timedelta对象

时间:2012-01-18 08:03:13

标签: python datetime formatting

我对python很新。 我有两个日期时间对象。我需要计算它们之间的时间值,然后以特定的格式显示输出。

Alpha_TimeObj = datetime.datetime(int(AlphaTime.strftime('%Y')), int(AlphaTime.strftime('%m')), int(AlphaTime.strftime('%d')), int(AlphaTime.strftime('%H')), int(AlphaTime.strftime('%M')), int(AlphaTime.strftime('%S')))
Beta_TimeObj = datetime.datetime(int(BetaTime.strftime('%Y')), int(BetaTime.strftime('%m')), int(BetaTime.strftime('%d')), int(BetaTime.strftime('%H')), int(BetaTime.strftime('%M')), int(BetaTime.strftime('%S')))
Turnaround_TimeObj = Beta_TimeObj  - Alpha_TimeObj 

此Turnaround_TimeObj时间增量的一个示例是“2天,22:13:45”。 我想格式化输出,但我无法这样做。

print Turnaround_TimeObj.strftime('%H hrs %M mins %S secs')

不起作用。

我知道这样做的一种方法是将其转换为秒,然后进行divmodding以获得所需的格式。 如在;

totalSeconds = Turnaround_TimeObj.seconds
hours, remainder = divmod(totalSeconds, 3600)
minutes, seconds = divmod(remainder, 60)
print '%s:%s:%s' % (hours, minutes, seconds)

但我想知道我是否可以使用任何日期时间函数(如strftime)在一行中完成。

编辑:实际转换为秒也不起作用。 如果我使用

将时间增量“1天,3:42:54”转换为秒
totalSeconds = Turnaround_TimeObj.seconds

totalSeconds值显示为13374而不是99774. ie。它无视“日”的价值。

7 个答案:

答案 0 :(得分:46)

  

但我想知道我是否可以使用任何日期时间函数(如strftime)在一行中完成。

据我所知,timedelta没有内置方法可以做到这一点。如果您经常这样做,您可以创建自己的功能,例如

def strfdelta(tdelta, fmt):
    d = {"days": tdelta.days}
    d["hours"], rem = divmod(tdelta.seconds, 3600)
    d["minutes"], d["seconds"] = divmod(rem, 60)
    return fmt.format(**d)

用法:

>>> print strfdelta(delta_obj, "{days} days {hours}:{minutes}:{seconds}")
1 days 20:18:12
>>> print strfdelta(delta_obj, "{hours} hours and {minutes} to go")
20 hours and 18 to go

如果您想使用更接近strftime使用的字符串格式,我们可以使用string.Template

from string import Template

class DeltaTemplate(Template):
    delimiter = "%"

def strfdelta(tdelta, fmt):
    d = {"D": tdelta.days}
    d["H"], rem = divmod(tdelta.seconds, 3600)
    d["M"], d["S"] = divmod(rem, 60)
    t = DeltaTemplate(fmt)
    return t.substitute(**d)

用法:

>>> print strfdelta(delta_obj, "%D days %H:%M:%S")
1 days 20:18:12
>>> print strfdelta(delta_obj, "%H hours and %M to go")
20 hours and 18 to go

  

totalSeconds值显示为13374而不是99774. ie。它忽略了" day"值。

请注意,在上面的示例中,您可以使用timedelta.days来获取" day"值。

或者,从Python 2.7开始,timedelta有一个total_seconds()方法,它返回持续时间中包含的总秒数。

答案 1 :(得分:15)

在Python2.7或更高版本中,您可以使用total_seconds方法:

import datetime as dt

turnaround = dt.timedelta(days = 1, hours = 3, minutes = 42, seconds = 54)

total_seconds = int(turnaround.total_seconds())
hours, remainder = divmod(total_seconds,60*60)
minutes, seconds = divmod(remainder,60)

print('{} hrs {} mins {} secs'.format(hours,minutes,seconds))

产量

27 hrs 42 mins 54 secs

在Python2.6或更早版本中,您可以自己计算total_seconds

total_seconds = turnaround.seconds + turnaround.days*24*60*60

(对于更通用的公式,包括微秒,请参阅上面的链接。)

答案 2 :(得分:14)

Shawn Chin的答案很好,但是它的一个问题是,如果你跳过你的格式中的特定元素(如他的第二个例子中只有几小时和几分钟......不是几天或几秒)那么那个时间就消失了从你的结果。您可以修改它以修复该问题,并通过仅处理实际出现在格式字符串中的关键字来获得更准确的结果。

class DeltaTemplate(Template):
    delimiter = '%'

def strfdelta(tdelta, fmt):
    d = {}
    l = {'D': 86400, 'H': 3600, 'M': 60, 'S': 1}
    rem = int(tdelta.total_seconds())

    for k in ( 'D', 'H', 'M', 'S' ):
        if "%{}".format(k) in fmt:
            d[k], rem = divmod(rem, l[k])

    t = DeltaTemplate(fmt)
    return t.substitute(**d)

用法:

>>> print strfdelta(delta_obj, "%D days %H:%M:%S")
1 days 20:18:12
>>> print strfdelta(delta_obj, "%H hours and %M to go")
44 hours and 18 to go

虽然格式化方式不灵活,因为你不能应用任何转换字符串并结束这样丑陋的事情:

>>> delta_obj = timedelta(minutes=5, seconds=2)
>>> print strfdelta(delta_obj, "%H:%M:%S")
0:5:2

但是,您可以采用相同的方法并将其应用于string.Formatter而不是string.Template并获得更好的效果。

from string import Formatter

def strfdelta(tdelta, fmt):
    f = Formatter()
    d = {}
    l = {'D': 86400, 'H': 3600, 'M': 60, 'S': 1}
    k = map( lambda x: x[1], list(f.parse(fmt)))
    rem = int(tdelta.total_seconds())

    for i in ('D', 'H', 'M', 'S'):
        if i in k and i in l.keys():
            d[i], rem = divmod(rem, l[i])

    return f.format(fmt, **d)

用法:

>>> delta_obj = timedelta(days=1, hours=20, minutes=18, seconds=12)
>>> print strfdelta(delta_obj, "{D} days {H}:{M}:{S}")
1 days 20:18:12
>>> print strfdelta(delta_obj, "{H} hours and {M} to go") 
44 hours and 18 to go

>>> delta_obj = timedelta(minutes=5, seconds=2)                     
>>> print strfdelta(delta_obj, "{H:02}h{M:02}m{S:02}s")  
00h05m02s
>>> print strfdelta(delta_obj, "{H:02}:{M:02}:{S:02}")
00:05:02

答案 3 :(得分:11)

Shawn Chin answer上的一个轻微变体 - 也解决了由issue引发的后续mpouncett - 以领先的小时,分​​钟和秒来填充零以确保所有3个元素都使用2个位置(更符合strftime中这些字段的规范):

from string import Template

class DeltaTemplate(Template):
    delimiter = "%"

def strfdelta(tdelta, fmt):
    d = {"D": tdelta.days}
    hours, rem = divmod(tdelta.seconds, 3600)
    minutes, seconds = divmod(rem, 60)
    d["H"] = '{:02d}'.format(hours)
    d["M"] = '{:02d}'.format(minutes)
    d["S"] = '{:02d}'.format(seconds)
    t = DeltaTemplate(fmt)
    return t.substitute(**d)

以下是对某些示例值的测试:

from datetime import timedelta
for seconds in [0, 1, 59, 60, 61, 3599, 3600, 3601]:
    print strfdelta(timedelta(0, seconds), '%H:%M:%S')

这是输出:

00:00:00
00:00:01
00:00:59
00:01:00
00:01:01
00:59:59
01:00:00
01:00:01

答案 4 :(得分:7)

您可以使用具有更友好的relativedelta对象的dateutil模块。

import dateutil
import datetime

alpha = datetime.datetime(2012, 1, 16, 6, 0)
beta = datetime.datetime(2012, 1, 18, 10, 42, 57, 230301)
delta = dateutil.relativedelta(beta, alpha)

这会为您提供一个看起来像

的对象增量
>>> delta
relativedelta(days=+2, hours=+4, minutes=+42, seconds=+57, microseconds=+230301)

然后你可以做

print('turnaround %i hrs %i mins %i secs' % (delta.days * 24 + delta.hours, delta.minutes, delta.seconds)

答案 5 :(得分:3)

def to_time(seconds):
  delta = datetime.timedelta(seconds=seconds)
  return str(delta.days) + 'd ' + (datetime.datetime.utcfromtimestamp(0) + delta).strftime('%H:%M')

答案 6 :(得分:2)

以上答案似乎没有处理时间值(例如由pytz为UTC的“左”时区生成)。作为per documentation,timedelta对象被标准化,负时间delta由负day实例属性表示。来自文档:

  

请注意,负值的标准化最初可能会令人惊讶。

那个

  

timedelta对象的字符串表示与其内部表示类似地进行规范化。这导致了负时间的一些不寻常的结果。

例如:

>>> td = timedelta(seconds=-30)
>>> str(td)
'-1 day, 23:59:30'
>>> repr(td)
'datetime.timedelta(-1, 86370)'

鉴于此示例timedelta,Shawn Chin’s accepted answergumption’s answer都返回'23:59:30'mpounsett’s answer '-1:59:30'

我认为为了以更易读的方式打印负时间和正时间,我们需要明确处理时区对象的符号和绝对值:

def strfdelta(td, fmt):

    # Get the timedelta’s sign and absolute number of seconds.
    sign = "-" if td.days < 0 else "+"
    secs = abs(td).total_seconds()

    # Break the seconds into more readable quantities.
    days, rem = divmod(secs, 86400)  # seconds per day: 24 * 60 * 60
    hours, rem = divmod(rem, 3600)  # seconds per hour: 60 * 60
    mins, secs = divmod(rem, 60)

    # Format (as per above answers) and return the result string.
    t = DeltaTemplate(fmt)
    return t.substitute(
        s=sign,
        D="{:d}".format(int(days)),
        H="{:02d}".format(int(hours)),
        M="{:02d}".format(int(mins)),
        S="{:02d}".format(int(secs)),
        )

此函数返回更易读的字符串表示形式:

>>> strfdelta(td, "%s%H:%M:%S")  # Note that %s refers to the timedelta’s sign.
'-00:00:30'
>>> strfdelta(timedelta(days=-1), "%s%D %H:%M:%S")
'-1 00:00:00'
>>> strfdelta(timedelta(days=-1, minutes=5), "%s%D %H:%M:%S")
'-0 23:55:00'
>>> strfdelta(timedelta(days=-1, minutes=-5), "%s%D %H:%M:%S")
'-1 00:05:00'

......或者在更实际的时区背景下:

>>> import pytz
>>> import datetime
>>> td = pytz.timezone("Canada/Newfoundland").utcoffset(datetime.datetime.now())
>>> td
datetime.timedelta(-1, 77400)
>>> strfdelta(td, fmt="%s%H:%M")
'-02:30'
>>> td = pytz.timezone("Australia/Eucla").utcoffset(datetime.datetime.now())
>>> td
datetime.timedelta(0, 31500)
>>> strfdelta(td, fmt="%s%H:%M")
'+08:45'