如何在Python中将datetime.timedelta转换为分钟、小时?

91

我得到了一个像这样的 start_date:

from django.utils.timezone import utc
import datetime

start_date = datetime.datetime.utcnow().replace(tzinfo=utc)
end_date = datetime.datetime.utcnow().replace(tzinfo=utc)
duration = end_date - start_date

我得到的输出结果如下:

datetime.timedelta(0, 5, 41038)

我该如何将它转换为正常的时间格式,例如:

10分钟,1小时之类的形式


3
可能是 Convert a timedelta to days, hours and minutes 的重复问题。 - Anto
1
这么多人查找这个内容说明 datetime 模块应该拥有这个基本功能... - theQuestionMan
10个回答

175

虽然timedelta对象没有内置的格式化程序,但自己编写格式化程序非常容易:

days, seconds = duration.days, duration.seconds
hours = days * 24 + seconds // 3600
minutes = (seconds % 3600) // 60
seconds = seconds % 60

或者等价地,如果您使用的是 Python 2.7+ 或 3.2+ 版本:

seconds = duration.total_seconds()
hours = seconds // 3600
minutes = (seconds % 3600) // 60
seconds = seconds % 60
现在你可以按照自己的需求打印它:
'{} minutes, {} hours'.format(minutes, hours)
例如:
def convert_timedelta(duration):
    days, seconds = duration.days, duration.seconds
    hours = days * 24 + seconds // 3600
    minutes = (seconds % 3600) // 60
    seconds = (seconds % 60)
    return hours, minutes, seconds
td = datetime.timedelta(2, 7743, 12345)
hours, minutes, seconds = convert_timedelta(td)
print '{} minutes, {} hours'.format(minutes, hours)

这将打印:

9 minutes, 50 hours
如果您想要获得“10分钟,1小时”而不是“10分钟,1个小时”,那么您也需要手动进行更改:
print '{} minute{}, {} hour{}'.format(minutes, 's' if minutes != 1 else '',
                                      hours, 's' if minutes != 1 else '')

或者你可能想编写一个english_plural函数来代替重复自己的's',而不是手动添加。

从您的评论中可以看出,您实际上希望保持每天分开。这更容易:

def convert_timedelta(duration):
    days, seconds = duration.days, duration.seconds
    hours = seconds // 3600
    minutes = (seconds % 3600) // 60
    seconds = (seconds % 60)
    return days, hours, minutes, seconds

如果您想将此转换为单个值以存储在数据库中,然后将该单个值转换回格式,请执行以下操作:

def dhms_to_seconds(days, hours, minutes, seconds):
    return (((days * 24) + hours) * 60 + minutes) * 60 + seconds

def seconds_to_dhms(seconds):
    days = seconds // (3600 * 24)
    hours = (seconds // 3600) % 24
    minutes = (seconds // 60) % 60
    seconds = seconds % 60
    return days, hours, minutes, seconds

因此,将其综合起来:

def store_timedelta_in_database(thingy, duration):
    seconds = dhms_to_seconds(*convert_timedelta(duration))
    db.execute('INSERT INTO foo (thingy, duration) VALUES (?, ?)',
               thingy, seconds)
    db.commit()

def print_timedelta_from_database(thingy):
    cur = db.execute('SELECT duration FROM foo WHERE thingy = ?', thingy)
    seconds = int(cur.fetchone()[0])
    days, hours, minutes, seconds = seconds_to_dhms(seconds)
    print '{} took {} minutes, {} hours, {} days'.format(thingy, minutes, hours, days)

34
如果我们只需要打印形式为[D day[s], ][H]H:MM:SS[.UUUUUU]的字符串,那么就不需要定制辅助函数。 timedelta对象支持str()操作可以做到这一点。它甚至可以在Python 2.6中运行。
>>> from datetime import timedelta
>>> timedelta(seconds=90136)
datetime.timedelta(1, 3736)
>>> str(timedelta(seconds=90136))
'1 day, 1:02:16'

3
除了问题奇怪的输出格式要求(我认为这不是真正的要求,只是提问者表达问题有困难),这应该是被接受的答案。 - Peter Mortensen

28

datetime.timedelta 表示两个日期之间的差异,而不是一个日期本身。它仅以天、秒和微秒的形式表示,因为较大的时间单位(如月份和年份)无法清晰地分解(例如30天是1个月还是0.9677个月?)。

如果您想将 timedelta 转换为小时和分钟,则可以使用 total_seconds() 方法获取总秒数,然后进行一些计算:

x = datetime.timedelta(1, 5, 41038)  # Interval of 1 day and 5.41038 seconds
secs = x.total_seconds()
hours = int(secs / 3600)
minutes = int(secs / 60) % 60

8

我认为自己计算不是一个好主意。

如果你只想要一个漂亮的输出,可以使用str()函数将其转换成字符串,或者直接使用print()函数。

如果需要进一步使用小时和分钟,可以使用datetime.strptime()解析为datetime对象(并使用datetime.time()方法提取时间部分),例如:

import datetime

delta = datetime.timedelta(seconds=10000)
time_obj = datetime.datetime.strptime(str(delta),'%H:%M:%S').time()

4

只需使用strftime :)

类似这样的:

my_date = datetime.datetime(2013, 1, 7, 10, 31, 34, 243366, tzinfo=<UTC>)
print(my_date.strftime("%Y, %d %B"))

编辑了你的问题以格式化时间差后,你可以使用以下代码:
def timedelta_tuple(timedelta_object):
   return timedelta_object.days, timedelta_object.seconds//3600, (timedelta_object.seconds//60)%60

4
# Try this code
from datetime import timedelta

class TimeDelta(timedelta):
    def __str__(self):
        _times = super(TimeDelta, self).__str__().split(':')
        if "," in _times[0]:
            _hour = int(_times[0].split(',')[-1].strip())
            if _hour:
                _times[0] += " hours" if _hour > 1 else " hour"
            else:
                _times[0] = _times[0].split(',')[0]
        else:
            _hour = int(_times[0].strip())
            if _hour:
                _times[0] += " hours" if _hour > 1 else " hour"
            else:
                _times[0] = ""
        _min = int(_times[1])
        if _min:
            _times[1] += " minutes" if _min > 1 else " minute"
        else:
            _times[1] = ""
        _sec = int(_times[2])
        if _sec:
            _times[2] += " seconds" if _sec > 1 else " second"
        else:
            _times[2] = ""
        return ", ".join([i for i in _times if i]).strip(" ,").title()


# Test
>>> str(TimeDelta(seconds=10))
'10 Seconds'
>>> str(TimeDelta(seconds=60))
'01 Minute'
>>> str(TimeDelta(seconds=90))
'01 Minute, 30 Seconds'
>>> str(TimeDelta(seconds=3000))
'50 Minutes'
>>> str(TimeDelta(seconds=3600))
'1 Hour'
>>> str(TimeDelta(seconds=3690))
'1 Hour, 01 Minute, 30 Seconds'
>>> str(TimeDelta(seconds=3660))
'1 Hour, 01 Minute'
>>> str(TimeDelta(seconds=3630))
'1 Hour, 30 Seconds'
>>> str(TimeDelta(seconds=3600*20))
'20 Hours'
>>> str(TimeDelta(seconds=3600*20 + 3000))
'20 Hours, 50 Minutes'
>>> str(TimeDelta(seconds=3600*20 + 3630))
'21 Hours, 30 Seconds'
>>> str(TimeDelta(seconds=3600*20 + 3660))
'21 Hours, 01 Minute'
>>> str(TimeDelta(seconds=3600*20 + 3690))
'21 Hours, 01 Minute, 30 Seconds'
>>> str(TimeDelta(seconds=3600*24))
'1 Day'
>>> str(TimeDelta(seconds=3600*24 + 10))
'1 Day, 10 Seconds'
>>> str(TimeDelta(seconds=3600*24 + 60))
'1 Day, 01 Minute'
>>> str(TimeDelta(seconds=3600*24 + 90))
'1 Day, 01 Minute, 30 Seconds'
>>> str(TimeDelta(seconds=3600*24 + 3000))
'1 Day, 50 Minutes'
>>> str(TimeDelta(seconds=3600*24 + 3600))
'1 Day, 1 Hour'
>>> str(TimeDelta(seconds=3600*24 + 3630))
'1 Day, 1 Hour, 30 Seconds'
>>> str(TimeDelta(seconds=3600*24 + 3660))
'1 Day, 1 Hour, 01 Minute'
>>> str(TimeDelta(seconds=3600*24 + 3690))
'1 Day, 1 Hour, 01 Minute, 30 Seconds'
>>> str(TimeDelta(seconds=3600*24*2))
'2 Days'
>>> str(TimeDelta(seconds=3600*24*2 + 9999))
'2 Days, 2 Hours, 46 Minutes, 39 Seconds'

3
我定义了自己的辅助函数,将时间差对象转换为“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)

2

解决方法之一是从时间差转换为秒开始创建一个相对时间。可以使用time.gmtime(...)方法将秒转换成纪元以来的秒数:

>>> time.strftime("%H:%M:%S",time.gmtime(36901))  # secs = 36901
'10:15:01'

就是这样了!(注:这里有一个链接,可以查看time.strftime()的格式说明符,以便将差异截断为任何单位,根据需要进行调整。...)

值得注意的是,这种技术也是判断当前时区是否处于夏令时的好方法。(它提供了0或1小时的偏移量,基本上可以解释为布尔值。)

import datetime
import pytz
import time

pacific=pytz.timezone('US/Pacific')
now=datetime.datetime.now()
# pacific.dst(now).total_seconds() yields 3600 secs. [aka 1 hour]
time.strftime("%-H", time.gmtime(pacific.dst(now).total_seconds()))
'1'

这可以转换成一个方法is_standard_time(...),其中1表示真,0表示假。

1

您想以那种格式打印日期吗?这是Python文档:http://docs.python.org/2/library/datetime.html#strftime-strptime-behavior

>>> a = datetime.datetime(2013, 1, 7, 10, 31, 34, 243366)
>>> print a.strftime('%Y %d %B, %M:%S%p')
>>> 2013 07 January, 31:34AM

对于时间差:

>>> a =  datetime.timedelta(0,5,41038)
>>> print '%s seconds, %s microseconds' % (a.seconds, a.microseconds)

但请注意,您应确保它具有相关的值。对于上述情况,它没有小时和分钟值,您应该从秒钟开始计算。


谢谢@abarnert。看起来他更新了问题。我也更新了答案。 - jinghli

-1
datetime.timedelta(hours=1, minutes=10)
#python 2.7

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接