Getting human-readable time interval strings

Here’s a neat little problem that came up yesterday. We’ve got some code using this common idiom to time a function call:

start_time = time.time()
do_something_long_running()
end_time = time.time()

print "It took {} seconds to execute this".format(end_time - start_time)

We want to make the elapsed time in seconds more human-readable. The first thing we can do is use the string-format mini language to round the number to two decimal points:

start_time = time.time()
do_something_long_running()
end_time = time.time()

print "It took {:.2f} seconds to execute this".format(end_time - start_time)

This is better! But for values that last longer than a few minutes, just displaying seconds is not particularly human friendly. What about minutes?

start_time = time.time()
do_something_long_running()
end_time = time.time()
print "It took {} minutes to execute this".format((end_time - start_time) / 60.)

Well now we’ve just pushed the problem out to the next unit mark. So how about hours?

start_time = time.time()
do_something_long_running()
end_time = time.time()

print "It took {} hours to execute this".format((end_time - start_time) / 3600.)

Great! But for function calls that cost in the range of a few seconds to a few minutes, all the significant digits get swallowed up in the rounding to hours. What we want is a nice, constant, H:MM:SS.SS format that can be easy to search against in log files and gives human-friendly reading. Similar to what we do above with dividing seconds by 60 to get minutes, and by 3600 (60 * 60) to get hours, we can write a function that does it all for us. We need to do an extra trick with the modulo operator, but otherwise this is straightforward:

def hms_string(sec_elapsed):
    h = int(sec_elapsed / (60 * 60))
    m = int((sec_elapsed % (60 * 60)) / 60)
    s = sec_elapsed % 60.
    return "{}:{:>02}:{:>05.2f}".format(h, m, s)
# End hms_string

start_time = time.time()
do_something_long_running()
end_time = time.time()
print "It took {} to execute this".format(hms_string(end_time - start_time))

And the hms_string function could work with a datetime.timedelta as well, just pass in timedelta.total_seconds() as the value to hms_string:

start_time = datetime.datetime.now()
do_something_long_running()
end_time = datetime.datetime.now()
seconds_elapsed = (end_time - start_time).total_seconds()
print "It took {} to execute this".format(hms_string(seconds_elapsed))

I recommend you check out the string format mini-language’s documentation — you can do all sorts of interesting things with it.

5 thoughts on “Getting human-readable time interval strings

  1. personally, I use this:

    import timeit
    execusionTime = timeit.Timer(setup=’from __main__ import do_something_long_running()
    ‘, stmt= do_something_long_running()
    ‘)
    print execusionTime.timeit(1)

  2. Pingback: Thiiink: Ideas, Imagination, and Innovation in GIS

  3. Pingback: Decorator Approach for Process Timing « Thiiink: Ideas, Imagination, and Innovation in GIS

  4. I have found that using the datetime module instead of time allows easy formatting of intervals by default.
    start = datetime.datetime.now()
    ….
    print datetime.datetime.now() – start # nicely formatted HH:MM:SS.00

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s