Here's a fun challenge, converting seconds into a specially formatted HH:MM:SS string -- Here's what I did, what would you do?

A few weeks ago I was working on a feature where I wanted to output human readable time spans, similar to what you might see in Youtube’s video player for keeping track of the time.

For example, 0:40, 1:50, 12:50 and so on.

I wrote a blog post that defines the spec in more detail and included both a Python and Elixir code example of how I solved the problem. There’s also a code solution from Greg Vaughn because he happened to be on Slack at the time of when I asked a question around solving this problem.

I thought maybe it would be interesting to post it here just to see how others would solve the problem. I have a feeling there’s probably 20 different ways this could be solved and the last time I posted something like this (a request for others to post their code to solve a real world problem) it had a lot of great submissions and discussions.

The spec and code examples can be found here: If you only care about the code, skim around. You’ll find the code blocks in a few seconds.

Feel free to post your code here.

1 Like

Here’s my approach. Posting it as a Gist to avoid accidentally spoiling the solution.


My take using strftime in Elixir master (upcoming v1.11):


Great idea, and nice code.

Does it pass the test suite at

It’s hard to tell from looking at it without running it, but how does it handle tracks less than 1 minute with adding a zero based minute 0:XX, only zero padding the non-left most cases or not showing the HH: for tracks less than 1 hour?

Using one of my ex_cldr libs. Similar approach to José’s. The function remove_leading_zeros/1 would probably apply to that code as well.

defmodule TimeFormat do
  def format(seconds) do
    |> Time.add(seconds)
    |> Cldr.Time.to_string!(format: "HH:mm:ss")
    |> remove_leading_zeros

  def remove_leading_zeros("00:0" <> rest), do: rest
  def remove_leading_zeros("00:" <> rest), do: rest
  def remove_leading_zeros("0" <> rest), do: rest
  def remove_leading_zeros(other), do: other

Doesn’t this and @josevalim approach will work only when there is less than 24 hours? If it is the case (so the amount of seconds is less than 86400) then one can use:

def format(seconds) do
  |> :calendar.seconds_to_time()
  |> do_format()
  |> List.to_string()

defp do_format({0, m, s}), do: :io_lib.format("~B:~2..0B", [m, s])
defp do_format({h, m, s}), do: :io_lib.format("~B:~2..0B:~2..0B", [h, m, s])