Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Limiting significant digits in formatted durations

I am timing some unpredictable I/O. This code

started := time.Now()
time.Sleep(123456789 * time.Nanosecond) // unpredictable process    
fmt.Printf("%v", time.Since(started))

Produces

123.456789ms

I like the automatic selection and printing of unit scale (ms, μs, ns etc) because I don't know in advance whether the timed operation takes microseconds, milliseconds or seconds to complete.

I don't like the precision - I'd prefer to report only two or three significant digits. Is there a simple way to limit the precision in the formatting directive %v or similar?

like image 588
RedGrittyBrick Avatar asked Jan 01 '23 14:01

RedGrittyBrick


1 Answers

Foreword: I released this utility in github.com/icza/gox, see timex.Round().


I don't think there's a simple way because when printed using the default format (e.g. %v), Duration.String() is called to produce the string representation. It returns a string value, so formatting options like number of fraction digits are not applicable anymore.

One way to control the resulting fraction digits is to truncate or round the duration before printing it, using Duration.Truncate() or Duration.Round().

Of course the unit to which the duration should be truncated or rounded to depends on the duration's value, but the logic is not that hard:

var divs = []time.Duration{
    time.Duration(1), time.Duration(10), time.Duration(100), time.Duration(1000)}

func round(d time.Duration, digits int) time.Duration {
    switch {
    case d > time.Second:
        d = d.Round(time.Second / divs[digits])
    case d > time.Millisecond:
        d = d.Round(time.Millisecond / divs[digits])
    case d > time.Microsecond:
        d = d.Round(time.Microsecond / divs[digits])
    }
    return d
}

Let's test it with different durations:

ds := []time.Duration{
    time.Hour + time.Second + 123*time.Millisecond, // 1h0m1.123s
    time.Hour + time.Second + time.Microsecond,     // 1h0m1.000001s
    123456789 * time.Nanosecond,                    // 123.456789ms
    123456 * time.Nanosecond,                       // 123.456µs
    123 * time.Nanosecond,                          // 123ns
}

for _, d := range ds {
    fmt.Printf("%-15v", d)
    for digits := 0; digits <= 3; digits++ {
        fmt.Printf("%-15v", round(d, digits))

    }
    fmt.Println()
}

Output will be (try it on the Go Playground):

duration       0 digits       1 digit        2 digits       3 digits
-----------------------------------------------------------------------
1h0m1.123s     1h0m1s         1h0m1.1s       1h0m1.12s      1h0m1.123s     
1h0m1.000001s  1h0m1s         1h0m1s         1h0m1s         1h0m1s         
123.456789ms   123ms          123.5ms        123.46ms       123.457ms      
123.456µs      123µs          123.5µs        123.46µs       123.456µs      
123ns          123ns          123ns          123ns          123ns       
like image 111
icza Avatar answered Jan 05 '23 18:01

icza