105

How do I convert an int (number of seconds) to the formats mm:ss or hh:mm:ss?

I need to do this with Python code (and if possible in a Django template).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131

10 Answers10

219

I can't believe any of the many answers gives what I'd consider the "one obvious way to do it" (and I'm not even Dutch...!-) -- up to just below 24 hours' worth of seconds (86399 seconds, specifically):

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

Doing it in a Django template's more finicky, since the time filter supports a funky time-formatting syntax (inspired, I believe, from PHP), and also needs the datetime module, and a timezone implementation such as pytz, to prep the data. For example:

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

Depending on your exact needs, it might be more convenient to define a custom filter for this formatting task in your app.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 4
    while it's a minor issue, which is possibly irrelevant for the OP `time.strftime('%H:%M:%S', time.gmtime(864001))` return a nasty surprise. – SilentGhost Sep 06 '09 at 14:35
  • 6
    Yep, only works up for one day -- as does your solution of course, which breaks down differently instead of "wrapping around", since `str(datetime.timedelta(seconds=86400))` says "1 day";-) – Alex Martelli Sep 06 '09 at 14:42
  • :) I don't think that my solution *breaks down*, it still provides the most-readable output, since OP hasn't participated in the discussion around here, I'm free to assume that that's exactly what he wants. – SilentGhost Sep 06 '09 at 16:29
  • Re *"...any of the many answers"*: Do you mean *"...none of the many answers"*? – Peter Mortensen Sep 24 '19 at 12:02
  • 1
    As @AlexMartelli says, this doesn't work correctly if the number of seconds is more than a day. Also, `str(datetime.timedelta(seconds=))` is more elegant – Granny Aching Dec 19 '19 at 20:54
  • I don't get the 'dutch' thing :( Also, I'm not dutch either. – Stefano Dec 16 '21 at 15:33
114
>>> a = datetime.timedelta(seconds=65)
datetime.timedelta(0, 65)
>>> str(a)
'0:01:05'
SilentGhost
  • 307,395
  • 66
  • 306
  • 293
  • 4
    h:mm:sss isn't hh:mm:ss. – Glenn Maynard Sep 05 '09 at 23:06
  • 5
    It doesn't answer his question, or offer anything that would lead him to a good answer to his question--at best you'd have to manually prepend a 0 or strip off "0:", which isn't a good solution at all (depends on timedelta.__str__ behavior which isn't specified anywhere as far as I know, so it could change). So, yes. – Glenn Maynard Sep 05 '09 at 23:19
  • 4
    i think you're just missing the point, OP needs number of seconds in **readable** format. that's what matters. – SilentGhost Sep 05 '09 at 23:32
  • 12
    @Glenn: Dude, you're killing me from up on your idealistic high horse there. It *does* answer his question, even if it's not the model of perfection. (Manually having to append or prepend a "0" is not exactly an onerous burden.) – Matt Howell Sep 05 '09 at 23:36
  • 4
    He didn't say "format it something like this", he gave specific formats. I can't count the times I've formatted that way. Editing the string means you're depending on the specific behavior of timedelta's string formatting (if s[0:2] == "0:": ...), which is a bad idea. Maybe timedelta won't decide to format it as "h.mm.ss" in some locales now, but nothing says it can't in the future. It's simply a poor approach. – Glenn Maynard Sep 05 '09 at 23:41
  • 2
    I really like this, because it can cope with seconds > one day in a nice way and can even handle fractions of seconds: `str(datetime.timedelta(seconds=1111111.11))` results in '12 days, 20:38:31.110000'. Exactly what I was looking for :) – luator Sep 11 '15 at 13:55
  • So, I found this trying to solve the question, and this worked perfect for me. 1106236 seconds became "12 days, 19:17:16" – Merovex Aug 19 '20 at 11:42
  • Also see [Format timedelta to string](https://stackoverflow.com/q/538666) – djvg Apr 17 '23 at 10:28
30

Read up on the datetime module.

SilentGhost's answer has the details my answer leaves out and is reposted here:

>>> a = datetime.timedelta(seconds=65)
datetime.timedelta(0, 65)
>>> str(a)
'0:01:05'
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Matt Howell
  • 15,750
  • 7
  • 49
  • 56
  • @sth: SilentGhost has the deets. – Matt Howell Sep 05 '09 at 23:07
  • I don't think anything in datetime does what he wants. There's nothing like timedelta.strftime, so even if he stores his "number of seconds" as a timedelta (which he probably should), he'd still have to do the formatting himself. Converting it to a time would be a hack, since he does seem to have an amount of time (timedelta) and not a time of day (time). – Glenn Maynard Sep 05 '09 at 23:09
  • @Glenn: What's wrong with the str() representation? – Matt Howell Sep 05 '09 at 23:13
  • 2
    @Glenn: from the question it's quite clear that formatting requirement is rather fluid. And it's much easier to just convert to string and be happy with `h:mm:ss` then to multiply dirty hacks trying to get `hh:mm:ss`. – SilentGhost Sep 05 '09 at 23:15
  • 1
    His question doesn't look fluid to me; it asks how to convert seconds to two specific formats, and almost everyone is responding with answers that simply don't do what he asked. Formatting this as he asked does not require anything approaching a hack. – Glenn Maynard Sep 05 '09 at 23:30
  • 2
    @GlennMaynard If "hh" is required, a simple `.zfill(7)` will take care of it. – Skippy le Grand Gourou Jan 28 '21 at 14:55
27

Code that does what was requested, with examples, and showing how cases he didn't specify are handled:

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"

You can--and probably should--store this as a timedelta rather than an int, but that's a separate issue and timedelta doesn't actually make this particular task any easier.

Glenn Maynard
  • 55,829
  • 10
  • 121
  • 131
15

You can calculate the number of minutes and hours from the number of seconds by simple division:

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

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

Here // is Python's integer division.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
sth
  • 222,467
  • 53
  • 283
  • 367
12

If you use divmod, you are immune to different flavors of integer division:

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

Prints:

63:20
01:03:20
01:03:20.123
uniquegino
  • 1,841
  • 1
  • 12
  • 11
PaulMcG
  • 62,419
  • 16
  • 94
  • 130
1

Not being a Python person, but the easiest without any libraries is just:

total   = 3800
seconds = total % 60
total   = total - seconds
hours   = total / 3600
total   = total - (hours * 3600)
mins    = total / 60
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
gaqzi
  • 3,707
  • 3
  • 30
  • 30
1

Just be careful when dividing by 60: division between integers returns an integer -> 12/60 = 0 unless you import division from future. The following is copy and pasted from Python 2.6.2:

IDLE 2.6.2      
>>> 12/60
0
>>> from __future__ import division
>>> 12/60
0.20000000000000001
Francesco
  • 3,200
  • 1
  • 34
  • 46
0

If you need to do this a lot, you can precalculate all possible strings for number of seconds in a day:

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

Now conversion of seconds to format string is a fast indexed lookup:

print hhmmss[12345]

prints

'03:25:45'

EDIT:

Updated to 2020, removing Py2 compatibility ugliness, and 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))

How much memory does this take? sys.getsizeof of a list won't do, since it will just give us the size of the list and its str refs, but not include the memory of the strs themselves:

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

prints:

5,657,616

What if we just had one big str? Every value is exactly 8 characters long, so we can slice into this str and get the correct str for second X of the day:

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

Did that save any space?

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

prints:

691,249

Reduced to about this much:

print(str_size / list_size)

prints:

0.12218026108523448

On the performance side, this looks like a classic memory vs. CPU tradeoff:

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

On my machine I get:

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
PaulMcG
  • 62,419
  • 16
  • 94
  • 130
  • This is not only wasteful of memory, but also unpleasant to read and unlikely to serve any kind of performance improvement. – Null511 Aug 20 '20 at 16:09
  • Wow, it has been almost 10 years since I wrote this, sometimes frivolous answers can give some opportunities for learning some new things - thanks for the comment! – PaulMcG Aug 20 '20 at 19:25
-2

Besides the fact that Python has built in support for dates and times (see bigmattyh's response), finding minutes or hours from seconds is easy:

minutes = seconds / 60
hours = minutes / 60

Now, when you want to display minutes or seconds, MOD them by 60 so that they will not be larger than 59

Ed S.
  • 122,712
  • 22
  • 185
  • 265