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

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

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

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


当前回答

我知道这是一个老问题,但我使用datetime.utcfromtimestamp()来解决这个问题。它接受秒数并返回一个datetime,该datetime可以像任何其他datetime一样格式化。

duration = datetime.utcfromtimestamp(end - begin)
print duration.strftime('%H:%M')

只要您停留在时间部分的合法范围内,这就应该工作,即它不会返回1234:35,因为小时<= 23。

其他回答

>>> 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部分把它重新组合在一起。

按照上面Joe的示例值,我将使用模算术运算符,因此:

td = datetime.timedelta(hours=10.56)
td_str = "%d:%d" % (td.seconds/3600, td.seconds%3600/60)

注意,Python中的整数除法默认是四舍五入;如果想要更显式,可以适当使用math.floor()或math.ceil()。

Timedelta到字符串,用于打印运行时间信息。

def strfdelta_round(tdelta, round_period='second'):
  """timedelta to string,  use for measure running time
  attend period from days downto smaller period, round to minimum period
  omit zero value period  
  """
  period_names = ('day', 'hour', 'minute', 'second', 'millisecond')
  if round_period not in period_names:
    raise Exception(f'round_period "{round_period}" invalid, should be one of {",".join(period_names)}')
  period_seconds = (86400, 3600, 60, 1, 1/pow(10,3))
  period_desc = ('days', 'hours', 'mins', 'secs', 'msecs')
  round_i = period_names.index(round_period)
  
  s = ''
  remainder = tdelta.total_seconds()
  for i in range(len(period_names)):
    q, remainder = divmod(remainder, period_seconds[i])
    if int(q)>0:
      if not len(s)==0:
        s += ' '
      s += f'{q:.0f} {period_desc[i]}'
    if i==round_i:
      break
    if i==round_i+1:
      s += f'{remainder} {period_desc[round_i]}'
      break
    
  return s

例如,自动省略零前导周期:

>>> td = timedelta(days=0, hours=2, minutes=5, seconds=8, microseconds=3549)
>>> strfdelta_round(td, 'second')
'2 hours 5 mins 8 secs'

或者省略中间的零周期:

>>> td = timedelta(days=2, hours=0, minutes=5, seconds=8, microseconds=3549)
>>> strfdelta_round(td, 'millisecond')
'2 days 5 mins 8 secs 3 msecs'

或舍入至分钟,省略以下分钟:

>>> td = timedelta(days=1, hours=2, minutes=5, seconds=8, microseconds=3549)
>>> strfdelta_round(td, 'minute')
'1 days 2 hours 5 mins'

下面是一个通用函数,用于将timedelta对象或常规数字(以秒或分钟等形式)转换为格式化良好的字符串。我采用了mpounsett对一个重复问题的出色回答,使其更加灵活,提高了可读性,并添加了文档。

你会发现这是迄今为止最灵活的答案,因为它允许你:

动态自定义字符串格式,而不是硬编码。 省略特定的时间间隔没有问题(参见下面的例子)。

功能:

from string import Formatter
from datetime import timedelta

def strfdelta(tdelta, fmt='{D:02}d {H:02}h {M:02}m {S:02}s', inputtype='timedelta'):
    """Convert a datetime.timedelta object or a regular number to a custom-
    formatted string, just like the stftime() method does for datetime.datetime
    objects.

    The fmt argument allows custom formatting to be specified.  Fields can 
    include seconds, minutes, hours, days, and weeks.  Each field is optional.

    Some examples:
        '{D:02}d {H:02}h {M:02}m {S:02}s' --> '05d 08h 04m 02s' (default)
        '{W}w {D}d {H}:{M:02}:{S:02}'     --> '4w 5d 8:04:02'
        '{D:2}d {H:2}:{M:02}:{S:02}'      --> ' 5d  8:04:02'
        '{H}h {S}s'                       --> '72h 800s'

    The inputtype argument allows tdelta to be a regular number instead of the  
    default, which is a datetime.timedelta object.  Valid inputtype strings: 
        's', 'seconds', 
        'm', 'minutes', 
        'h', 'hours', 
        'd', 'days', 
        'w', 'weeks'
    """

    # Convert tdelta to integer seconds.
    if inputtype == 'timedelta':
        remainder = int(tdelta.total_seconds())
    elif inputtype in ['s', 'seconds']:
        remainder = int(tdelta)
    elif inputtype in ['m', 'minutes']:
        remainder = int(tdelta)*60
    elif inputtype in ['h', 'hours']:
        remainder = int(tdelta)*3600
    elif inputtype in ['d', 'days']:
        remainder = int(tdelta)*86400
    elif inputtype in ['w', 'weeks']:
        remainder = int(tdelta)*604800

    f = Formatter()
    desired_fields = [field_tuple[1] for field_tuple in f.parse(fmt)]
    possible_fields = ('W', 'D', 'H', 'M', 'S')
    constants = {'W': 604800, 'D': 86400, 'H': 3600, 'M': 60, 'S': 1}
    values = {}
    for field in possible_fields:
        if field in desired_fields and field in constants:
            values[field], remainder = divmod(remainder, constants[field])
    return f.format(fmt, **values)

演示:

>>> td = timedelta(days=2, hours=3, minutes=5, seconds=8, microseconds=340)

>>> print strfdelta(td)
02d 03h 05m 08s

>>> print strfdelta(td, '{D}d {H}:{M:02}:{S:02}')
2d 3:05:08

>>> print strfdelta(td, '{D:2}d {H:2}:{M:02}:{S:02}')
 2d  3:05:08

>>> print strfdelta(td, '{H}h {S}s')
51h 308s

>>> print strfdelta(12304, inputtype='s')
00d 03h 25m 04s

>>> print strfdelta(620, '{H}:{M:02}', 'm')
10:20

>>> print strfdelta(49, '{D}d {H}h', 'h')
2d 1h

我想这样做,所以写了一个简单的函数。它对我来说非常有用,而且非常通用(支持年到微秒,以及任何粒度级别,例如,你可以选择“2天4小时48分钟”和“2天4小时”和“2天4.8小时”等。

def pretty_print_timedelta(t, max_components=None, max_decimal_places=2):
''' 
Print a pretty string for a timedelta. 
For example datetime.timedelta(days=2, seconds=17280) will be printed as '2 days, 4 hours, 48 minutes'. Setting max_components to e.g. 1 will change this to '2.2 days', where the 
number of decimal points can also be set. 
'''
time_scales = [timedelta(days=365), timedelta(days=1), timedelta(hours=1), timedelta(minutes=1), timedelta(seconds=1), timedelta(microseconds=1000), timedelta(microseconds=1)]
time_scale_names_dict = {timedelta(days=365): 'year',  
                         timedelta(days=1): 'day', 
                         timedelta(hours=1): 'hour', 
                         timedelta(minutes=1): 'minute', 
                         timedelta(seconds=1): 'second', 
                         timedelta(microseconds=1000): 'millisecond', 
                         timedelta(microseconds=1): 'microsecond'}
count = 0
txt = ''
first = True
for scale in time_scales:
    if t >= scale: 
        count += 1
        if count == max_components:
            n = t / scale
        else:
            n = int(t / scale)
            
        t -= n*scale
        
        n_txt = str(round(n, max_decimal_places))
        if n_txt[-2:]=='.0': n_txt = n_txt[:-2]
        txt += '{}{} {}{}'.format('' if first else ', ', n_txt, time_scale_names_dict[scale], 's' if n>1 else '', )
        if first:
            first = False
        
        
if len(txt) == 0: 
    txt = 'none'
return txt