Python 将timedelta格式化为字符串

Python 将timedelta格式化为字符串,python,string,datetime,format,timedelta,Python,String,Datetime,Format,Timedelta,格式化datetime.timedelta对象时遇到问题 以下是我想做的: 我有一个对象列表,对象类的一个成员是显示事件持续时间的timedelta对象。我想以小时:分钟的格式显示该持续时间 我尝试了各种各样的方法来做这件事,但我遇到了困难。我目前的方法是为返回小时和分钟的对象向类中添加方法。我可以通过将timedelta.seconds除以3600并四舍五入得到小时数。我在获取剩余的秒数并将其转换为分钟时遇到问题 顺便说一下,我正在使用GoogleAppengine和Django模板进行演示

格式化
datetime.timedelta
对象时遇到问题

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

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

顺便说一下,我正在使用GoogleAppengine和Django模板进行演示

>>> 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()。

您可以使用str()将timedelta转换为字符串。下面是一个例子:

import datetime
start = datetime.datetime(2009,2,10,14,00)
end   = datetime.datetime(2009,2,10,16,00)
delta = end-start
print(str(delta))
# prints 2:00:00

如您所知,您可以通过访问
.seconds
属性从timedelta对象获取总的_秒数

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

s=13420
小时,余数=divmod(s,3600)
分,秒=divmod(余数,60)
打印“{:02}:{:02}:{:02}”。格式(整数(小时)、整数(分钟)、整数(秒))
#结果:03:43:40
或者,您可以使用模和减法的组合将其转换为小时和余数:

#任意秒数
s=13420
#小时数
小时=s//3600
#剩余秒数
s=s-(小时*3600)
#会议记录
分钟=s//60
#剩余秒数
秒=秒-(分钟*60)
#总时间
打印“{:02}:{:02}:{:02}”。格式(整数(小时)、整数(分钟)、整数(秒))
#结果:03:43:40

My
datetime.timedelta
对象超过了一天。所以这里还有一个问题。以上所有讨论假设不到一天。
timedelta
实际上是天、秒和微秒的元组。上面的讨论应该像joe那样使用
td.seconds
,但是如果你有天数,它就不包括在seconds值中

我得到的时间跨度介于2个datetimes和打印天数和小时之间

span = currentdt - previousdt
print '%d,%d\n' % (span.days,span.seconds/3600)

提问者想要一个比典型的更好的格式:

  >>> import datetime
  >>> datetime.timedelta(seconds=41000)
  datetime.timedelta(0, 41000)
  >>> str(datetime.timedelta(seconds=41000))
  '11:23:20'
  >>> str(datetime.timedelta(seconds=4102.33))
  '1:08:22.330000'
  >>> str(datetime.timedelta(seconds=413302.33))
  '4 days, 18:48:22.330000'
所以,实际上有两种格式,一种是天为0,省略,另一种是文本“n天,h:m:s”。但是,秒可能有分数,并且打印输出中没有前导零,所以列很乱

如果你喜欢的话,这是我的常规:

def printNiceTimeDelta(stime, etime):
    delay = datetime.timedelta(seconds=(etime - stime))
    if (delay.days > 0):
        out = str(delay).replace(" days, ", ":")
    else:
        out = "0:" + str(delay)
    outAr = out.split(':')
    outAr = ["%02d" % (int(float(x))) for x in outAr]
    out   = ":".join(outAr)
    return out
这将以dd:hh:mm:ss格式返回输出:

00:00:00:15
00:00:00:19
02:01:31:40
02:01:32:22
我确实考虑过增加年数,但这是留给读者的练习,因为输出在1年以上是安全的:

>>> str(datetime.timedelta(seconds=99999999))
'1157 days, 9:46:39'

他已经有了一个timedelta对象,为什么不使用它的内置方法total_seconds()将其转换为秒,然后使用divmod()获取小时和分钟

hours, remainder = divmod(myTimeDelta.total_seconds(), 3600)
minutes, seconds = divmod(remainder, 60)

# Formatted only for hours and minutes as requested
print '%s:%s' % (hours, minutes)
无论时间差是几天还是几年,这都有效

t1 = datetime.datetime.strptime(StartTime, "%H:%M:%S %d-%m-%y")

t2 = datetime.datetime.strptime(EndTime, "%H:%M:%S %d-%m-%y")

return str(t2-t1)
因此:

StartTime = '15:28:53 21-07-13'
EndTime = '15:32:40 21-07-13'
返回:

'0:03:47'

我对工作中加班计算的结果也有类似的问题。该值应始终以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

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

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

只要你停留在法律范围内的时间部分,这应该工作,也就是说,它不会返回1234∶35,因为小时是

我会认真考虑奥卡姆剃刀的方法:< /P>

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))

两年过去了,我喜欢这种语言

请检查此函数-它将timedelta对象转换为字符串“HH:MM:SS”

def format_timedelta(td):
    hours, remainder = divmod(td.total_seconds(), 3600)
    minutes, seconds = divmod(remainder, 60)
    hours, minutes, seconds = int(hours), int(minutes), int(seconds)
    if hours < 10:
        hours = '0%s' % int(hours)
    if minutes < 10:
        minutes = '0%s' % minutes
    if seconds < 10:
        seconds = '0%s' % seconds
    return '%s:%s:%s' % (hours, minutes, seconds)
def格式\u时间差(td):
小时,余数=divmod(td.total_seconds(),3600)
分,秒=divmod(余数,60)
小时,分钟,秒=整数(小时),整数(分钟),整数(秒)
如果小时数小于10:
小时数='0%s'%int(小时)
如果分钟数小于10:
分钟数='0%s'%1分钟
如果秒数小于10:
秒数='0%s'%s!'
返回“%s:%s:%s%”(小时、分钟、秒)
这将产生:

>>> from datetime import timedelta
>>> localize_timedelta(timedelta(days=3660, minutes=500))
'10 years 10 days 8 hours 20 minutes'
我个人使用图书馆是为了:

>>> import datetime
>>> humanize.naturalday(datetime.datetime.now())
'today'
>>> humanize.naturalday(datetime.datetime.now() - datetime.timedelta(days=1))
'yesterday'
>>> humanize.naturalday(datetime.date(2007, 6, 5))
'Jun 05'
>>> humanize.naturaldate(datetime.date(2007, 6, 5))
'Jun 05 2007'
>>> humanize.naturaltime(datetime.datetime.now() - datetime.timedelta(seconds=1))
'a second ago'
>>> humanize.naturaltime(datetime.datetime.now() - datetime.timedelta(seconds=3600))
'an hour ago'
当然,它并没有给出你想要的答案(确实是,
str(timeA-timeB)
),但我发现,一旦超过几个小时,显示器很快就会变得不可读。
humanize
支持更大的值,这些值是人类可读的,并且也可以很好地本地化


它的灵感来自Django的
contrib.humanize
模块,显然,既然您使用的是Django,那么您可能应该使用它。

这里有一个通用函数,用于转换
timedelta
对象或常规数字(以秒或分等形式)我接受了一个重复的问题,使它更加灵活,提高了可读性,并添加了文档

import datetime
td = datetime.timedelta(hours=2, minutes=10, seconds=5)
print(td)
print(datetime.datetime.strftime(datetime.datetime.strptime(str(td), "%H:%M:%S"), "%H:%M"))

Output:
2:10:05
02:10
您会发现,这是迄今为止最灵活的答案,因为它允许您:

  • 动态定制字符串格式,而不是硬编码
  • L
    from django.utils.translation import ngettext
    
    def localize_timedelta(delta):
        ret = []
        num_years = int(delta.days / 365)
        if num_years > 0:
            delta -= timedelta(days=num_years * 365)
            ret.append(ngettext('%d year', '%d years', num_years) % num_years)
    
        if delta.days > 0:
            ret.append(ngettext('%d day', '%d days', delta.days) % delta.days)
    
        num_hours = int(delta.seconds / 3600)
        if num_hours > 0:
            delta -= timedelta(hours=num_hours)
            ret.append(ngettext('%d hour', '%d hours', num_hours) % num_hours)
    
        num_minutes = int(delta.seconds / 60)
        if num_minutes > 0:
            ret.append(ngettext('%d minute', '%d minutes', num_minutes) % num_minutes)
    
        return ' '.join(ret)
    
    >>> from datetime import timedelta
    >>> localize_timedelta(timedelta(days=3660, minutes=500))
    '10 years 10 days 8 hours 20 minutes'
    
    >>> import datetime
    >>> humanize.naturalday(datetime.datetime.now())
    'today'
    >>> humanize.naturalday(datetime.datetime.now() - datetime.timedelta(days=1))
    'yesterday'
    >>> humanize.naturalday(datetime.date(2007, 6, 5))
    'Jun 05'
    >>> humanize.naturaldate(datetime.date(2007, 6, 5))
    'Jun 05 2007'
    >>> humanize.naturaltime(datetime.datetime.now() - datetime.timedelta(seconds=1))
    'a second ago'
    >>> humanize.naturaltime(datetime.datetime.now() - datetime.timedelta(seconds=3600))
    'an hour ago'
    
    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
    
    t = str(timedeltaobj) 
    
    print t[:-3]
    
    import datetime
    hours = datetime.timedelta(hours=16, minutes=30)
    print((datetime.datetime(1,1,1) + hours).strftime('%H:%M'))
    
    import humanfriendly
    from datetime import timedelta
    delta = timedelta(seconds = 321)
    humanfriendly.format_timespan(delta)
    
    '5 minutes and 21 seconds'
    
    @app_template_filter()
    def diffTime(end, start):
        diff = (end - start).total_seconds()
        d = int(diff / 86400)
        h = int((diff - (d * 86400)) / 3600)
        m = int((diff - (d * 86400 + h * 3600)) / 60)
        s = int((diff - (d * 86400 + h * 3600 + m *60)))
        if d > 0:
            fdiff = f'{d}d {h}h {m}m {s}s'
        elif h > 0:
            fdiff = f'{h}h {m}m {s}s'
        elif m > 0:
            fdiff = f'{m}m {s}s'
        else:
            fdiff = f'{s}s'
        return fdiff
    
    import datetime
    td = datetime.timedelta(hours=2, minutes=10, seconds=5)
    print(td)
    print(datetime.datetime.strftime(datetime.datetime.strptime(str(td), "%H:%M:%S"), "%H:%M"))
    
    Output:
    2:10:05
    02:10
    
    >>> 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
    
    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
    
    def period(delta, pattern):
        d = {'d': delta.days}
        d['h'], rem = divmod(delta.seconds, 3600)
        d['m'], d['s'] = divmod(rem, 60)
        return pattern.format(**d)
    
    >>> td = timedelta(seconds=123456789)
    >>> period(td, "{d} days {h}:{m}:{s}")
    '1428 days 21:33:9'
    >>> period(td, "{h} hours, {m} minutes and {s} seconds, {d} days")
    '21 hours, 33 minutes and 9 seconds, 1428 days'
    
    1 years 11 months 2 weeks 3 days 01:09:56.00354900211096
    23 months 2 weeks 3 days 00:12:20.0035
    8.008549 seconds 8 milliseconds 549 microseconds
    8 seconds 8 milliseconds 549 microseconds
    115d 17h 46m 40s
    
    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'
    
    >>> import datetime
    >>> dt0 = datetime.datetime(1,1,1)
    >>> td = datetime.timedelta(minutes=34, hours=12, seconds=56)
    >>> (dt0+td).strftime('%X')
    '12:34:56'
    >>> (dt0+td).strftime('%M:%S')
    '34:56'
    >>> (dt0+td).strftime('%H:%M')
    '12:34'
    >>>
    
    from pandas import Timestamp, Timedelta
    
    (Timedelta("2 hours 30 min") + Timestamp("00:00:00")).strftime("%H:%M")
    
    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