Re: Factor

Factor: the language, the theory, and the practice.

Time Spans for Humans

Friday, May 14, 2010

#time

When dealing with time spans or durations, it is nice to be able to render elapsed time in a way that is easy for the human eye to read.

In some cases, this could mean translating the way you might say it or write it (two years, ten days, four hours, thirty minutes, and fifteen seconds) or a more concise form (2y 10d 4h 30m 15s). The concise form has become popular in some web applications, and solutions exist for both in most languages including python, perl, and java. I thought I would build a word in Factor to calculate it.

First, we need some other vocabularies:

USING: kernel make math math.parser sequences ;

The make vocabulary can be used to execute a quotation that builds a sequence of values at runtime. This can be a little confusing for those new to Factor. Basically, you provide it a quotation that periodically emits values (by calling the , word) that are collected into the sequence type you provide. For example:

IN: scratchpad [ 1 , 2 , 3 , ] { } make .
{ 1 2 3 }

Next, we need an algorithm. One way is to iteratively divmod the number of seconds into each category (e.g., years, weeks, days, hours, minutes, seconds) and count it if the number in the category is non-zero.

: elapsed-time ( seconds -- string )
    dup 0 < [ "negative seconds" throw ] when [
        {
            { 60 "s" }
            { 60 "m" }
            { 24 "h" }
            {  7 "d" }
            { 52 "w" }
            {  f "y" }
        } [
            [ first [ /mod ] [ dup ] if* ] [ second ] bi swap
            dup 0 > [ number>string prepend , ] [ 2drop ] if
        ] each drop
    ] { } make [ "0s" ] [ reverse " " join ] if-empty ;

And then to see it work:

IN: scratchpad 123456 elapsed-time .
"1d 10h 17m 36s"