I was looking through some old code I had written and found this small and cryptic method for converting a number of seconds into a string containing of this format “hh:mm:ss” and I will describe how it works. First, the code:
def seconds_to_time(secs)
hms = [3600, 60].inject([secs]) {|x, y| x += x.pop.divmod(y)}
return hms.map {|y| "%02d" % y}.join(":")
end
This method leverages the behavior of the Enum.inject method and the Numeric.divmod method to creatively convert the seconds into an array containing hours, minutes and seconds. It then utilizes the Enum.map method to join these values into a recognizable time string. Here is some sample output:
puts seconds_to_time(63628) 17:40:28
The inject method is passed in a starting value and on each iteration the return value of the previous iteration is passed again. This parameter is commonly called an accumulator. The accumulator shows up in the provided block as the first block variable called x. The initial value of this accumulator is an array containing the original seconds. The second parameter to the block (y) is the value from the original array that is the focus of the current iteration. In this example y starts out as 3600.
In the first iteration this value is pop’d out of this array and the divmod method is called passing in the first value from the array that inject is being called on. This value is 3600 and represents the number of seconds in one hour. The return value of the divmod method is an array with the integer part as the first position and the remainder as the second position [17, 2428]. Since the original seconds were pop’d from x, x will now be empty and += will cause x to fill up with the 2 values that were returned from divmod. At the end of the first iteration of the inject method the accumulator variable will contain [17, 2428].
In iteration 2 y is set to 60 which is the number of seconds in a minute while x is set to the value of the accumulator from the previous iteration [17, 2428]. 2428 will be pop’d from this array and divmod will be called on this value with a parameter of y. The result of this call is the array [40, 28] which represents the number of minutes in 2428 seconds and the remainder. This array is then appended to x causing it to become [17, 40, 28] which is the end of our second iteration.
Since this is our last iteration the return value for the inject call will be the array [17, 40, 28]. This array is then map’d into an array of strings formatted as 2 digit integers. This step is needed to pad single digit positions with a leading zero. The final step is to call the join method to glue these pieces together with a colon.