在Python中将秒转换为hh:mm:ss

109

我该如何将一个整数(表示秒数)转换为mm:sshh:mm:ss格式?

我需要使用Python代码来实现此功能(如果可能的话,还可以在Django模板中实现)。

10个回答

222

我无法相信任何一个答案都可以给出我认为的“显而易见的方法”(我甚至不是荷兰人...!-)——最多只有23小时59分59秒的秒数(特定地86399秒):

>>> import time
>>> time.strftime('%H:%M:%S', time.gmtime(12345))
'03:25:45'

在Django模板中实现这个功能稍微有些麻烦,因为time过滤器支持奇怪的时间格式语法(我相信这是受PHP启发的),同时需要datetime模块和时区实现库pytz来准备数据。例如:

>>> from django import template as tt
>>> import pytz
>>> import datetime
>>> tt.Template('{{ x|time:"H:i:s" }}').render(
...     tt.Context({'x': datetime.datetime.fromtimestamp(12345, pytz.utc)}))
u'03:25:45'

根据您的需要,可能更方便在您的应用程序中定义一个自定义过滤器来执行此格式化任务。


5
虽然这只是一个小问题,对于提问者来说可能无关紧要,但 time.strftime('%H:%M:%S', time.gmtime(864001)) 返回了一个令人不悦的意外结果。 - SilentGhost
6
是的,只能工作一天 - 当然,您的解决方案也是如此,它不同于“环绕”,因为str(datetime.timedelta(seconds = 86400))表示“1天”;-) - Alex Martelli
我认为我的解决方案并没有“崩溃”,它仍然提供了最易读的输出,由于OP没有参与这里的讨论,我可以自由地假设这正是他想要的。 - SilentGhost
Re "...any of the many answers": 你是不是指的是 *"...没有任何一个答案"*? - Peter Mortensen
1
正如@AlexMartelli所说的那样,如果秒数超过一天,这种方法无法正确工作。使用str(datetime.timedelta(seconds=<number>))会更加优雅。 - Granny Aching
我不明白“荷兰”的事情 :( 而且,我也不是荷兰人。 - Stefano

114
>>> a = datetime.timedelta(seconds=65)
datetime.timedelta(0, 65)
>>> str(a)
'0:01:05'

5
h:mm:sss 不等同于 hh:mm:ss。 - Glenn Maynard
5
它没有回答他的问题,也没有提供任何能够引导他得到好答案的东西——最好的情况是你必须手动添加一个0或者去掉“0:”,但这并不是一个好的解决方案(这取决于timedelta.__str__的行为,据我所知,这在任何地方都没有被指定,因此它可能会改变)。所以,是的。 - Glenn Maynard
4
我认为你误解了重点,OP需要以可读格式提供秒数。这才是最重要的。 - SilentGhost
12
@Glenn: 伙计,你站在理想主义的高马上指责别人真的让我感到心累。这确实回答了他的问题,即使不是完美的模范。(手动添加或前置“0”并不是特别繁琐的负担。) - Matt Howell
4
他没有说“把它格式化成这样”,他给出了具体的格式。我已经无数次按照那种方式进行了格式化。编辑字符串意味着您依赖于timedelta字符串格式化的特定行为(如果s[0:2] ==“0:”:…),这是一个不好的想法。也许现在timedelta在某些语言环境下不会像“h.mm.ss”一样进行格式化,但没有任何东西能保证它将来不会这样做。这只是一个不好的方法。 - Glenn Maynard
显示剩余3条评论

29

请阅读 datetime 模块文档。

SilentGhost 的回答详细介绍了我的回答遗漏的内容,这里进行转载:

>>> a = datetime.timedelta(seconds=65)
datetime.timedelta(0, 65)
>>> str(a)
'0:01:05'

@sth:SilentGhost知道详情。 - Matt Howell
我认为datetime中没有任何东西能够满足他的需求。没有类似于timedelta.strftime的东西,所以即使他将“秒数”存储为timedelta(他可能应该这样做),他仍然需要自己进行格式化。将其转换为时间将是一种hack方法,因为他似乎拥有一段时间(timedelta)而不是一天中的时间(time)。 - Glenn Maynard
@Glenn:str()表示有什么问题吗? - Matt Howell
2
@Glenn:从问题中可以清楚地看出,格式要求相当灵活。将其转换为字符串并使用“h:mm:ss”更容易,而不是尝试使用肮脏的技巧来获取“hh:mm:ss”。 - SilentGhost
1
他的问题对我来说不太流畅;他问如何将秒转换为两种特定格式,几乎每个人都回答了根本不能满足他要求的答案。按照他的要求进行格式化不需要任何接近黑客的技巧。 - Glenn Maynard
2
@GlennMaynard 如果需要“hh”,一个简单的.zfill(7)就可以解决。 - Skippy le Grand Gourou

27

代码执行了要求的功能,提供了示例,并展示了如何处理他没有指定的情况:

def format_seconds_to_hhmmss(seconds):
    hours = seconds // (60*60)
    seconds %= (60*60)
    minutes = seconds // 60
    seconds %= 60
    return "%02i:%02i:%02i" % (hours, minutes, seconds)

def format_seconds_to_mmss(seconds):
    minutes = seconds // 60
    seconds %= 60
    return "%02i:%02i" % (minutes, seconds)

minutes = 60
hours = 60*60
assert format_seconds_to_mmss(7*minutes + 30) == "07:30"
assert format_seconds_to_mmss(15*minutes + 30) == "15:30"
assert format_seconds_to_mmss(1000*minutes + 30) == "1000:30"

assert format_seconds_to_hhmmss(2*hours + 15*minutes + 30) == "02:15:30"
assert format_seconds_to_hhmmss(11*hours + 15*minutes + 30) == "11:15:30"
assert format_seconds_to_hhmmss(99*hours + 15*minutes + 30) == "99:15:30"
assert format_seconds_to_hhmmss(500*hours + 15*minutes + 30) == "500:15:30"

你可以 - 而且很可能应该 - 将这个值存储为 timedelta 而不是 int,但这是一个独立的问题,并且使用 timedelta 实际上并不能使这个特定任务更容易。


2
你可以使用divmod来简化计算。 - unbeknown
3
不行,一点也不符合 Python 风格。 - Johannes Overmann

16

通过简单的除法,您可以从秒数计算出分钟数和小时数:

seconds = 12345
minutes = seconds // 60
hours = minutes // 60

print "%02d:%02d:%02d" % (hours, minutes % 60, seconds % 60)
print "%02d:%02d" % (minutes, seconds % 60)

这里的//是Python中的整数除法。


12
如果您使用divmod,则可以避免不同类型的整数除法:
# show time strings for 3800 seconds

# easy way to get mm:ss
print "%02d:%02d" % divmod(3800, 60)

# easy way to get hh:mm:ss
from functools import reduce
print "%02d:%02d:%02d" % \
    reduce(lambda ll,b : divmod(ll[0],b) + ll[1:],
        [(3800,),60,60])


# function to convert floating point number of seconds to
# hh:mm:ss.sss
def secondsToStr(t):
    return "%02d:%02d:%02d.%03d" % \
        reduce(lambda ll,b : divmod(ll[0],b) + ll[1:],
            [(round(t*1000),),1000,60,60])

print secondsToStr(3800.123)

输出:

63:20
01:03:20
01:03:20.123

1
修复 secondsToStr 函数的 bug:四舍五入毫秒。将“t1000”替换为“round(t1000)”。 - milahu

1

在进行60的除法时要小心:整数之间的除法会返回一个整数-> 12/60 = 0,除非你从未来导入除法。以下是从Python 2.6.2复制并粘贴的内容:

IDLE 2.6.2      
>>> 12/60
0
>>> from __future__ import division
>>> 12/60
0.20000000000000001

1

虽然我不是 Python 的专家,但最简单的方法就是不使用任何库:

total   = 3800
seconds = total % 60
total   = total - seconds
hours   = total / 3600
total   = total - (hours * 3600)
mins    = total / 60

0
如果您需要经常这样做,可以预先计算一天中所有可能的秒数字符串:
try:
    from itertools import product
except ImportError:
    def product(*seqs):
        if len(seqs) == 1:
            for p in seqs[0]:
                yield p,
        else:
            for s in seqs[0]:
                for p in product(*seqs[1:]):
                    yield (s,) + p

hhmmss = []
for (h, m, s) in product(range(24), range(60), range(60)):
    hhmmss.append("%02d:%02d:%02d" % (h, m, s))

现在将秒转换为格式字符串是一个快速的索引查找:

print hhmmss[12345]

打印

'03:25:45'

编辑:

已更新到2020年,删除了Py2兼容性问题和f-strings!

import sys
from itertools import product


hhmmss = [f"{h:02d}:{m:02d}:{s:02d}"
             for h, m, s in product(range(24), range(60), range(60))]

# we can still just index into the list, but define as a function
# for common API with code below
seconds_to_str = hhmmss.__getitem__

print(seconds_to_str(12345))

这需要多少内存?使用列表的sys.getsizeof无法计算,因为它只会给出列表及其字符串引用的大小,但不包括字符串本身的内存:
# how big is a list of 24*60*60 8-character strs?
list_size = sys.getsizeof(hhmmss) + sum(sys.getsizeof(s) for s in hhmmss)
print("{:,}".format(list_size))

打印:

5,657,616

如果我们只有一个大字符串会怎样?每个值都恰好为8个字符长,因此我们可以切片这个字符串并获取正确的第二个X的字符串:
hhmmss_str = ''.join([f"{h:02d}:{m:02d}:{s:02d}"
                      for h, m, s in product(range(24),
                                             range(60),
                                             range(60))])
def seconds_to_str(n):
    loc = n * 8
    return hhmmss_str[loc: loc+8]

print(seconds_to_str(12345))

这样做有节省空间吗?

# how big is a str of 24*60*60*8 characters?
str_size = sys.getsizeof(hhmmss_str)
print("{:,}".format(str_size))

打印:

691,249

缩小到大约这么多:

print(str_size / list_size)

打印:

0.12218026108523448

在性能方面,这看起来像是经典的内存与CPU之间的权衡。
import timeit

print("\nindex into pre-calculated list")
print(timeit.timeit("hhmmss[6]", '''from itertools import product; hhmmss = [f"{h:02d}:{m:02d}:{s:02d}"
                     for h, m, s in product(range(24),
                                             range(60),
                                             range(60))]'''))
print("\nget slice from pre-calculated str")
print(timeit.timeit("hhmmss_str[6*8:7*8]", '''from itertools import product; hhmmss_str=''.join([f"{h:02d}:{m:02d}:{s:02d}"
                     for h, m, s in product(range(24),
                                             range(60),
                                             range(60))])'''))

print("\nuse datetime.timedelta from stdlib")
print(timeit.timeit("timedelta(seconds=6)", "from datetime import timedelta"))
print("\ninline compute of h, m, s using divmod")
print(timeit.timeit("n=6;m,s=divmod(n,60);h,m=divmod(m,60);f'{h:02d}:{m:02d}:{s:02d}'"))

在我的电脑上,我得到的结果是:
index into pre-calculated list
0.0434853

get slice from pre-calculated str
0.1085147

use datetime.timedelta from stdlib
0.7625738

inline compute of h, m, s using divmod
2.0477764

这不仅浪费内存,而且阅读起来也很不舒服,不太可能提高任何性能。 - Null511
哇,我写下这篇文章已经快10年了,有时候一些轻松的回答也能给我们学习新事物的机会 - 感谢你的评论! - PaulMcG

-2
除了Python内置支持日期和时间(请参见bigmattyh的回答)之外,从秒钟中找到分钟或小时也很容易:
minutes = seconds / 60
hours = minutes / 60

现在,当您想要显示分钟或秒时,请对它们进行60的取模运算,以确保它们不会大于59。

1
分钟取模60,因此它始终小于60,所以小时等于0... - Guðmundur H
抱歉,我的意思是之后再执行那一步...我会进行修正。 - Ed S.
至少提供实现取模的具体代码。这个答案让我感觉到有点儿轻率,就好像在说:“嘿,一分钟有60秒——以防你不知道一样。” - user3761340

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