将timedelta格式化为字符串

问题:将timedelta格式化为字符串

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

这是我要执行的操作:我有一个对象列表,并且该对象类的成员之一是timedelta对象,该对象显示事件的持续时间。我想以小时:分钟的格式显示该持续时间。

我尝试了多种方法来执行此操作,但遇到了困难。我当前的方法是为返回小时和分钟的对象添加方法。我可以将timedelta.seconds除以3600并四舍五入来获得小时数。我在获取剩余秒数并将其转换为分钟时遇到麻烦。

顺便说一句,我将Google AppEngine与Django模板结合使用进行演示。

I’m having trouble formatting a datetime.timedelta object.

Here’s what I’m trying to do: I have a list of objects and one of the members of the class of the object is a timedelta object that shows the duration of an event. I would like to display that duration in the format of hours:minutes.

I have tried a variety of methods for doing this and I’m having difficulty. My current approach is to add methods to the class for my objects that return hours and minutes. I can get the hours by dividing the timedelta.seconds by 3600 and rounding it. I’m having trouble with getting the remainder seconds and converting that to minutes.

By the way, I’m using Google AppEngine with Django Templates for presentation.


回答 0

您可以使用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

You can just convert the timedelta to a string with str(). Here’s an example:

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

回答 1

如您所知,您可以通过访问.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

As you know, you can get the total_seconds from a timedelta object by accessing the .seconds attribute.

Python provides the builtin function divmod() which allows for:

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

or you can convert to hours and remainder by using a combination of modulo and subtraction:

# 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

回答 2

>>> 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个部分放回去。

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

Passing the timedelta object to the str() function calls the same formatting code used if we simply type print td. Since you don’t want the seconds, we can split the string by colons (3 parts) and put it back together with only the first 2 parts.


回答 3

def td_format(td_object):
    seconds = int(td_object.total_seconds())
    periods = [
        ('year',        60*60*24*365),
        ('month',       60*60*24*30),
        ('day',         60*60*24),
        ('hour',        60*60),
        ('minute',      60),
        ('second',      1)
    ]

    strings=[]
    for period_name, period_seconds in periods:
        if seconds > period_seconds:
            period_value , seconds = divmod(seconds, period_seconds)
            has_s = 's' if period_value > 1 else ''
            strings.append("%s %s%s" % (period_value, period_name, has_s))

    return ", ".join(strings)
def td_format(td_object):
    seconds = int(td_object.total_seconds())
    periods = [
        ('year',        60*60*24*365),
        ('month',       60*60*24*30),
        ('day',         60*60*24),
        ('hour',        60*60),
        ('minute',      60),
        ('second',      1)
    ]

    strings=[]
    for period_name, period_seconds in periods:
        if seconds > period_seconds:
            period_value , seconds = divmod(seconds, period_seconds)
            has_s = 's' if period_value > 1 else ''
            strings.append("%s %s%s" % (period_value, period_name, has_s))

    return ", ".join(strings)

回答 4

我个人使用该humanize库:

>>> 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它支持更大的值易于阅读,并且位置很好。

contrib.humanize显然,它是受Django 模块启发的,因此,由于您使用的是Django,您应该使用它。

I personally use the humanize library for this:

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

Of course, it doesn’t give you exactly the answer you were looking for (which is, indeed, str(timeA - timeB), but I have found that once you go beyond a few hours, the display becomes quickly unreadable. humanize has support for much larger values that are human-readable, and is also well localized.

It’s inspired by Django’s contrib.humanize module, apparently, so since you are using Django, you should probably use that.


回答 5

他已经有一个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)

无论时间增量是偶数天还是数年,这都有效。

He already has a timedelta object so why not use its built-in method total_seconds() to convert it to seconds, then use divmod() to get hours and minutes?

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)

This works regardless if the time delta has even days or years.


回答 6

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

您会发现,到目前为止,这是最灵活的答案,因为它可以使您:

  1. 即时自定义字符串格式,而不是对其进行硬编码。
  2. 留出一定的时间间隔没有问题(请参见下面的示例)。

功能:

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

Here is a general purpose function for converting either a timedelta object or a regular number (in the form of seconds or minutes, etc.) to a nicely formatted string. I took mpounsett’s fantastic answer on a duplicate question, made it a bit more flexible, improved readibility, and added documentation.

You will find that it is the most flexible answer here so far since it allows you to:

  1. Customize the string format on the fly instead of it being hard-coded.
  2. Leave out certain time intervals without a problem (see examples below).

Function:

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)

Demo:

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

回答 7

我知道这是一个古老的已回答问题,但我datetime.utcfromtimestamp()为此使用了。它需要秒数并返回datetime可以像其他格式一样设置的datetime

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

只要您停留在合法的时间范围内,它就应该起作用,即,当小时数小于等于23时,它不会返回1234:35。

I know that this is an old answered question, but I use datetime.utcfromtimestamp() for this. It takes the number of seconds and returns a datetime that can be formatted like any other datetime.

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

As long as you stay in the legal ranges for the time parts this should work, i.e. it doesn’t return 1234:35 as hours are <= 23.


回答 8

发问者想要比典型的更好的格式:

  >>> 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 days,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'

Questioner wants a nicer format than the typical:

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

So, really there’s two formats, one where days are 0 and it’s left out, and another where there’s text “n days, h:m:s”. But, the seconds may have fractions, and there’s no leading zeroes in the printouts, so columns are messy.

Here’s my routine, if you like it:

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

this returns output as dd:hh:mm:ss format:

00:00:00:15
00:00:00:19
02:01:31:40
02:01:32:22

I did think about adding years to this, but this is left as an exercise for the reader, since the output is safe at over 1 year:

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

回答 9

我会在这里认真考虑Occam的Razor方法:

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年过去了,我喜欢这种语言!

I would seriously consider the Occam’s Razor approach here:

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

This returns a string without the microseconds

If you want to regenerate the datetime.timedelta object, just do this:

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

2 years in, I love this language!


回答 10

我的datetime.timedelta物品超过一天。因此,这是另一个问题。以上所有讨论都假设不到一天。A timedelta实际上是天,秒和微秒的元组。上面的讨论应该使用td.seconds像joe一样,但是如果您有工作日,则它不包括在seconds值中。

我得到了2个日期时间与打印天数和小时之间的时间跨度。

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

My datetime.timedelta objects went greater than a day. So here is a further problem. All the discussion above assumes less than a day. A timedelta is actually a tuple of days, seconds and microseconds. The above discussion should use td.seconds as joe did, but if you have days it is NOT included in the seconds value.

I am getting a span of time between 2 datetimes and printing days and hours.

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

回答 11

我使用humanfriendlypython库来做到这一点,它工作得很好。

import humanfriendly
from datetime import timedelta
delta = timedelta(seconds = 321)
humanfriendly.format_timespan(delta)

'5 minutes and 21 seconds'

可在https://pypi.org/project/humanfriendly/

I used the humanfriendly python library to do this, it works very well.

import humanfriendly
from datetime import timedelta
delta = timedelta(seconds = 321)
humanfriendly.format_timespan(delta)

'5 minutes and 21 seconds'

Available at https://pypi.org/project/humanfriendly/


回答 12

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

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

注意,Python中的整数除法默认舍入;如果要更明确,请适当使用math.floor()或math.ceil()。

Following Joe’s example value above, I’d use the modulus arithmetic operator, thusly:

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

Note that integer division in Python rounds down by default; if you want to be more explicit, use math.floor() or math.ceil() as appropriate.


回答 13

def seconds_to_time_left_string(total_seconds):
    s = int(total_seconds)
    years = s // 31104000
    if years > 1:
        return '%d years' % years
    s = s - (years * 31104000)
    months = s // 2592000
    if years == 1:
        r = 'one year'
        if months > 0:
            r += ' and %d months' % months
        return r
    if months > 1:
        return '%d months' % months
    s = s - (months * 2592000)
    days = s // 86400
    if months == 1:
        r = 'one month'
        if days > 0:
            r += ' and %d days' % days
        return r
    if days > 1:
        return '%d days' % days
    s = s - (days * 86400)
    hours = s // 3600
    if days == 1:
        r = 'one day'
        if hours > 0:
            r += ' and %d hours' % hours
        return r 
    s = s - (hours * 3600)
    minutes = s // 60
    seconds = s - (minutes * 60)
    if hours >= 6:
        return '%d hours' % hours
    if hours >= 1:
        r = '%d hours' % hours
        if hours == 1:
            r = 'one hour'
        if minutes > 0:
            r += ' and %d minutes' % minutes
        return r
    if minutes == 1:
        r = 'one minute'
        if seconds > 0:
            r += ' and %d seconds' % seconds
        return r
    if minutes == 0:
        return '%d seconds' % seconds
    if seconds == 0:
        return '%d minutes' % minutes
    return '%d minutes and %d seconds' % (minutes, seconds)

for i in range(10):
    print pow(8, i), seconds_to_time_left_string(pow(8, i))


Output:
1 1 seconds
8 8 seconds
64 one minute and 4 seconds
512 8 minutes and 32 seconds
4096 one hour and 8 minutes
32768 9 hours
262144 3 days
2097152 24 days
16777216 6 months
134217728 4 years
def seconds_to_time_left_string(total_seconds):
    s = int(total_seconds)
    years = s // 31104000
    if years > 1:
        return '%d years' % years
    s = s - (years * 31104000)
    months = s // 2592000
    if years == 1:
        r = 'one year'
        if months > 0:
            r += ' and %d months' % months
        return r
    if months > 1:
        return '%d months' % months
    s = s - (months * 2592000)
    days = s // 86400
    if months == 1:
        r = 'one month'
        if days > 0:
            r += ' and %d days' % days
        return r
    if days > 1:
        return '%d days' % days
    s = s - (days * 86400)
    hours = s // 3600
    if days == 1:
        r = 'one day'
        if hours > 0:
            r += ' and %d hours' % hours
        return r 
    s = s - (hours * 3600)
    minutes = s // 60
    seconds = s - (minutes * 60)
    if hours >= 6:
        return '%d hours' % hours
    if hours >= 1:
        r = '%d hours' % hours
        if hours == 1:
            r = 'one hour'
        if minutes > 0:
            r += ' and %d minutes' % minutes
        return r
    if minutes == 1:
        r = 'one minute'
        if seconds > 0:
            r += ' and %d seconds' % seconds
        return r
    if minutes == 0:
        return '%d seconds' % seconds
    if seconds == 0:
        return '%d minutes' % minutes
    return '%d minutes and %d seconds' % (minutes, seconds)

for i in range(10):
    print pow(8, i), seconds_to_time_left_string(pow(8, i))


Output:
1 1 seconds
8 8 seconds
64 one minute and 4 seconds
512 8 minutes and 32 seconds
4096 one hour and 8 minutes
32768 9 hours
262144 3 days
2097152 24 days
16777216 6 months
134217728 4 years

回答 14

我在工作中加班计算的输出也有类似的问题。该值应始终以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到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'

I had a similar problem with the output of overtime calculation at work. The value should always show up in HH:MM, even when it is greater than one day and the value can get negative. I combined some of the shown solutions and maybe someone else find this solution useful. I realized that if the timedelta value is negative most of the shown solutions with the divmod method doesn’t work out of the box:

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 string:

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'

回答 15

import datetime
hours = datetime.timedelta(hours=16, minutes=30)
print((datetime.datetime(1,1,1) + hours).strftime('%H:%M'))
import datetime
hours = datetime.timedelta(hours=16, minutes=30)
print((datetime.datetime(1,1,1) + hours).strftime('%H:%M'))

回答 16

直接解决此问题的模板过滤器。内置函数int()从不舍入。F字符串(即f”)需要python 3.6。

@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

A straight forward template filter for this problem. The built-in function int() never rounds up. F-Strings (i.e. f”) require python 3.6.

@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

回答 17

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

This will produce:

>>> from datetime import timedelta
>>> localize_timedelta(timedelta(days=3660, minutes=500))
'10 years 10 days 8 hours 20 minutes'

回答 18

请检查此功能-它会将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)

Please check this function – it converts timedelta object into string ‘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)

回答 19

一支班轮。由于timedelta不提供datetime的strftime,因此请将timedelta带回datetime,然后使用stftime。

这不仅可以实现OP要求的格式Hours:Minutes,现在,如果您的需求更改为其他表示形式,则可以利用datetime的strftime的完整格式化功能。

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

这也解决了将时间增量格式化为H:MM:SS而不是HH:MM:SS的麻烦,这导致了我遇到这个问题,并且分享了我的解决方案。

One liner. Since timedeltas do not offer datetime’s strftime, bring the timedelta back to a datetime, and use stftime.

This can not only achieve the OP’s requested format Hours:Minutes, now you can leverage the full formatting power of datetime’s strftime, should your requirements change to another representation.

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

This also solves the annoyance that timedeltas are formatted into strings as H:MM:SS rather than HH:MM:SS, which lead me to this problem, and the solution I’ve shared.


回答 20

如果您已经有一个timedelta obj,则只需将该obj转换为字符串即可。删除字符串的最后3个字符并打印。这将截断秒数部分,并以小时:分钟的格式打印其余部分。

t = str(timedeltaobj) 

print t[:-3]

If you already have a timedelta obj then just convert that obj into string. Remove the last 3 characters of the string and print. This will truncate the seconds part and print the rest of it in the format Hours:Minutes.

t = str(timedeltaobj) 

print t[:-3]

回答 21

如果您恰好IPython在包装中(应该如此),则它具有(到目前为止,无论如何)持续时间非常长的格式化程序(以秒为单位)。那是在各个地方使用的,例如%%time细胞魔术。我喜欢它能短期产生的格式:

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

If you happen to have IPython in your packages (you should), it has (up to now, anyway) a very nice formatter for durations (in float seconds). That is used in various places, for example by the %%time cell magic. I like the format it produces for short durations:

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

回答 22

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

So for:

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

returns:

'0:03:47'

回答 23

感谢大家的帮助。我采纳了您的许多想法并将它们组合在一起,让我知道您的想法。

我向此类添加了两个方法:

def hours(self):
    retval = ""
    if self.totalTime:
        hoursfloat = self.totalTime.seconds / 3600
        retval = round(hoursfloat)
    return retval

def minutes(self):
    retval = ""
    if self.totalTime:
        minutesfloat = self.totalTime.seconds / 60
        hoursAsMinutes = self.hours() * 60
        retval = round(minutesfloat - hoursAsMinutes)
    return retval

在我的django中,我使用了这个(sum是对象,它在字典中):

<td>{{ sum.0 }}</td>    
<td>{{ sum.1.hours|stringformat:"d" }}:{{ sum.1.minutes|stringformat:"#02.0d" }}</td>

Thanks everyone for your help. I took many of your ideas and put them together, let me know what you think.

I added two methods to the class like this:

def hours(self):
    retval = ""
    if self.totalTime:
        hoursfloat = self.totalTime.seconds / 3600
        retval = round(hoursfloat)
    return retval

def minutes(self):
    retval = ""
    if self.totalTime:
        minutesfloat = self.totalTime.seconds / 60
        hoursAsMinutes = self.hours() * 60
        retval = round(minutesfloat - hoursAsMinutes)
    return retval

In my django I used this (sum is the object and it is in a dictionary):

<td>{{ sum.0 }}</td>    
<td>{{ sum.1.hours|stringformat:"d" }}:{{ sum.1.minutes|stringformat:"#02.0d" }}</td>