我在格式化日期时间时遇到了麻烦。timedelta对象。

这就是我想做的: 我有一个对象列表,对象类的成员之一是显示事件持续时间的timedelta对象。我想以小时:分钟的格式显示这个持续时间。

我尝试了各种方法来做这件事,但我有困难。我目前的方法是为返回小时和分钟的对象在类中添加方法。我可以通过除以time得到小时数。秒乘以3600,四舍五入。我在得到剩余的秒并将其转换为分钟时遇到了麻烦。

顺便说一下,我使用谷歌AppEngine和Django模板来表示。


当前回答

如您所知,您可以通过访问.seconds属性从timedelta对象中获得total_seconds。

Python提供了内置函数divmod(),它允许:

s = 13420
hours, remainder = divmod(s, 3600)
minutes, seconds = divmod(remainder, 60)
print('{:02}:{:02}:{:02}'.format(int(hours), int(minutes), int(seconds)))
# result: 03:43:40

或者你可以结合使用模和减法来转换小时和余数:

# arbitrary number of seconds
s = 13420
# hours
hours = s // 3600 
# remaining seconds
s = s - (hours * 3600)
# minutes
minutes = s // 60
# remaining seconds
seconds = s - (minutes * 60)
# total time
print('{:02}:{:02}:{:02}'.format(int(hours), int(minutes), int(seconds)))
# result: 03:43:40

其他回答

如果您的包中碰巧有IPython(您应该),那么它(到目前为止)有一个非常好的持续时间格式器(以浮点秒为单位)。它被用于许多地方,例如%%时间单元格魔法。我喜欢它在短时间内产生的格式:

>>> from IPython.core.magics.execution import _format_time
>>> 
>>> for v in range(-9, 10, 2):
...     dt = 1.25 * 10**v
...     print(_format_time(dt))

1.25 ns
125 ns
12.5 µs
1.25 ms
125 ms
12.5 s
20min 50s
1d 10h 43min 20s
144d 16h 13min 20s
14467d 14h 13min 20s

我在工作中遇到过类似的加班计算输出问题。该值应该始终以HH:MM显示,即使它大于一天并且该值可能为负值。我结合了一些展示的解决方案,也许其他人会发现这个解决方案很有用。我意识到,如果timedelta值为负,大多数divmod方法所显示的解决方案都不能开箱即用:

def td2HHMMstr(td):
  '''Convert timedelta objects to a HH:MM string with (+/-) sign'''
  if td < datetime.timedelta(seconds=0):
    sign='-'
    td = -td
  else:
    sign = ''
  tdhours, rem = divmod(td.total_seconds(), 3600)
  tdminutes, rem = divmod(rem, 60)
  tdstr = '{}{:}:{:02d}'.format(sign, int(tdhours), int(tdminutes))
  return tdstr

timedelta to HH:MM

td2HHMMstr(datetime.timedelta(hours=1, minutes=45))
'1:54'

td2HHMMstr(datetime.timedelta(days=2, hours=3, minutes=2))
'51:02'

td2HHMMstr(datetime.timedelta(hours=-3, minutes=-2))
'-3:02'

td2HHMMstr(datetime.timedelta(days=-35, hours=-3, minutes=-2))
'-843:02'

下面是一个stringify timedelta.total_seconds()的函数。它在python2和python3中工作。

def strf_interval(seconds):
    days, remainder = divmod(seconds, 86400)
    hours, remainder = divmod(remainder, 3600)
    minutes, seconds = divmod(remainder, 60)
    return '{} {} {} {}'.format(
            "" if int(days) == 0 else str(int(days)) + ' days',
            "" if int(hours) == 0 else str(int(hours)) + ' hours',
            "" if int(minutes) == 0 else str(int(minutes))  + ' mins',
            "" if int(seconds) == 0 else str(int(seconds))  + ' secs'
        )

示例输出:

>>> print(strf_interval(1))
   1 secs
>>> print(strf_interval(100))
  1 mins 40 secs
>>> print(strf_interval(1000))
  16 mins 40 secs
>>> print(strf_interval(10000))
 2 hours 46 mins 40 secs
>>> print(strf_interval(100000))
1 days 3 hours 46 mins 40 secs
>>> str(datetime.timedelta(hours=10.56))
10:33:36

>>> td = datetime.timedelta(hours=10.505) # any timedelta object
>>> ':'.join(str(td).split(':')[:2])
10:30

将timedelta对象传递给str()函数调用的格式化代码与输入print td相同。因为你不想要秒,我们可以用冒号分隔字符串(3部分),然后只用前2部分把它重新组合在一起。

在这里,我会认真考虑奥卡姆剃刀方法:

td = str(timedelta).split('.')[0]

这将返回一个没有微秒的字符串

如果要重新生成datetime。Timedelta对象,只需要这样做:

h,m,s = re.split(':', td)
new_delta = datetime.timedelta(hours=int(h),minutes=int(m),seconds=int(s))

2年了,我爱上了这门语言!