How to round a Duration?

1,500 views
Skip to first unread message

Gary Scarr

unread,
Sep 2, 2014, 11:54:27 AM9/2/14
to golan...@googlegroups.com
To log a time difference I initially coded:


start := time.Now()
...
fmt.Printf("...took %s\n", time.Since(start))


but that resulted in an output of 19.7996387s which is impressive in its resolution but I really only wanted a display resolution to the second.

So I thought the natural thing would be to call: 

time.Since(start).Round(time.Second) 

but there is no Round method on Duration, only on a Time.  So I resorted to:


start := time.Now().Round(time.Second)
...
end := := time.Now().Round(time.Second)
fmt.Printf("...took %s\n", end.Sub(start))


which gives me the requisite display of "20s" but isn't as clean or precise as rounding the duration itself.

Am I missing an obvious way of doing this or should there be a Round method on Duration?

Thanks,
Gary  



Jan Mercl

unread,
Sep 2, 2014, 11:58:01 AM9/2/14
to Gary Scarr, golang-nuts
On Tue, Sep 2, 2014 at 5:54 PM, Gary Scarr <sca...@gmail.com> wrote:

secs := (duration + time.Second/2) / time.Second

-j

Gary Scarr

unread,
Sep 2, 2014, 12:27:10 PM9/2/14
to golan...@googlegroups.com, sca...@gmail.com
Thanks Jan but that results in a display of "20ns" because of the inherent resolution of a Duration on my machine.  So yes, I can then multiply the whole thing by time.second and get 20s but it seems very ugly compared to a Round method on Duration:

duration := time.Since(start)
duration = ((duration + time.Second/2) / time.Second ) * time.Second

vs

time.Since(start).Round(time.Second) 

Gary

Jan Mercl

unread,
Sep 2, 2014, 12:34:58 PM9/2/14
to Gary Scarr, golang-nuts

Well, I forgot to mention that secs will contain an integer number of seconds (assumed "%ds" fmt.Printf format). The example should have used an explicit conversion to an int, though.

(phone)
-j

Ian Davis

unread,
Sep 2, 2014, 12:38:15 PM9/2/14
to golan...@googlegroups.com
 
On Tue, Sep 2, 2014, at 04:54 PM, Gary Scarr wrote:
 
Am I missing an obvious way of doing this or should there be a Round method on Duration?
 
A Duration is just an int64 number of nanoseconds, so you could do:
 
seconds := int64(time.Since(start)) / 1e9
 
That would give you seconds as an int64
 
Ian
 

egon

unread,
Sep 2, 2014, 12:51:18 PM9/2/14
to golan...@googlegroups.com


On Tuesday, 2 September 2014 18:54:27 UTC+3, Gary Scarr wrote:
To log a time difference I initially coded:


start := time.Now()
...
fmt.Printf("...took %s\n", time.Since(start))


but that resulted in an output of 19.7996387s which is impressive in its resolution but I really only wanted a display resolution to the second.

So I thought the natural thing would be to call: 

time.Since(start).Round(time.Second) 

but there is no Round method on Duration, only on a Time.  So I resorted to:


start := time.Now().Round(time.Second)
...
end := := time.Now().Round(time.Second)
fmt.Printf("...took %s\n", end.Sub(start))

start := time.Now()
...
diff := time.Since(start)
fmt.Printf("%v\n", diff.Nanoseconds()/time.Second.Nanoseconds())

Gary Scarr

unread,
Sep 2, 2014, 12:53:39 PM9/2/14
to golan...@googlegroups.com
Thanks but then I lose the inherent formatting ability of the Duration object.  Some of my durations run to hours and minutes (where even seconds are irrelevant) and I like the built-in logic that switches the display based upon the actual period.

Rob Pike

unread,
Sep 2, 2014, 1:15:52 PM9/2/14
to Gary Scarr, golan...@googlegroups.com
Just write the code.

-rob
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Gary Scarr

unread,
Sep 2, 2014, 1:20:57 PM9/2/14
to golan...@googlegroups.com
This wins the originality prize for use of time.Seconds.Nanoseconds().  My immediate thought was "how can you call a method on a constant?" but then I realized "in Go you can call a method on anything".  Unfortunately it also suffers from the limitation of Ian's solution since it ends up with an uint64.  It's apparent that I didn't make clear in my original post that I want to continue to have a Duration object (so that it can display longer durations) but round the output to seconds.
Gary

Matthew Zimmerman

unread,
Sep 2, 2014, 1:32:03 PM9/2/14
to Gary Scarr, golang-nuts
In (github.com/mzimmerman/racergo) I created a ```type HumanDuration
time.Duration``` then defined String() to display 5k race times.

func (hd HumanDuration) String() string {
seconds := time.Duration(hd).Seconds()
seconds -= float64(time.Duration(hd) / time.Minute * 60)
return fmt.Sprintf("%#02d:%#02d:%05.2f",
time.Duration(hd)/time.Hour, time.Duration(hd)/time.Minute%60,
seconds)
}

Your exact use case isn't known so this may match or not.

Jason Woods

unread,
Sep 2, 2014, 3:17:13 PM9/2/14
to Gary Scarr, golan...@googlegroups.com
I use:

now := time.Now()

... Stuff ...

duration := time.Since(now)
log.Printf("duration = %v", duration-(duration%time.Second))

To get it rounded to minute etc just change the modulus.

Is this what you want?

Jason


Andrew Bursavich

unread,
Sep 2, 2014, 5:52:57 PM9/2/14
to golan...@googlegroups.com, sca...@gmail.com
Here's a rounding function: http://play.golang.org/p/QHocTHl8iR

Gary Scarr

unread,
Sep 2, 2014, 11:16:04 PM9/2/14
to golan...@googlegroups.com, sca...@gmail.com
Bravo Andrew!!! 

This is indeed what I had been wanting: the automatic formatting range of the existing Duration with the arbitrary precision of the Time.Round function.  With all due respect to Rob with his "Just write the code", it didn't seem that obvious and I would certainly never have come up with anything this elegant.  My first instinct was along the lines of Jan's:( (dur + resolution/2)/resolution) * resolution but there's no hint of a 2 in your code so it will take me a while to grok it.  But it does far better than my simple rounding of both timing endpoints so it doesn't affect their precision.

As is often the case, after posing my "simple" question and then being asked "Is this what you want" it occurred to me that what might also be useful was to print a duration with a "sensible" precision like 2 or 3 digits.  Is there a way to use your function (other than a switch or if/else chain based on the value of d) that would achieve this?  I tried a very naive making r a function of d as here where n was intended to be the approx digits of precision but it failed horribly:
switch n {
case 1:
r = d / 10
case 2:
r = d / 100
case 3:
r = d / 1000
case 4:
r = d / 10000
case 5:
r = d / 100000
case 6:
r = d / 10000000
}

Thanks to you and all the others for your suggestions,
Gary

Harmen B

unread,
Sep 3, 2014, 4:15:12 AM9/3/14
to Gary Scarr, golang-nuts
Maybe you can do something with this:

fmt.Printf("Hello number: %.*f", 3, 12.3456789)


The '*' operator is explained in the fmt godoc.


--

Andrew Bursavich

unread,
Sep 3, 2014, 4:50:06 AM9/3/14
to golan...@googlegroups.com, sca...@gmail.com

On Tuesday, September 2, 2014 8:16:04 PM UTC-7, Gary Scarr wrote:
Bravo Andrew!!! 

This is indeed what I had been wanting: the automatic formatting range of the existing Duration with the arbitrary precision of the Time.Round function.  With all due respect to Rob with his "Just write the code", it didn't seem that obvious and I would certainly never have come up with anything this elegant.

With all due respect to Rob, my solution was elegant and quick to arrive at because I basically jacked it from the standard library. You mentioned Time.Round so I took a peek at the source. From there it just took a couple minutes. In my experience, if you want to do something similar to the standard library it's usually a good idea to start by reading it... and then maybe by stealing from it.
 

My first instinct was along the lines of Jan's:( (dur + resolution/2)/resolution) * resolution but there's no hint of a 2 in your code so it will take me a while to grok it.  But it does far better than my simple rounding of both timing endpoints so it doesn't affect their precision.

There's a hint of a 2 with "m+m" ;)

 
As is often the case, after posing my "simple" question and then being asked "Is this what you want" it occurred to me that what might also be useful was to print a duration with a "sensible" precision like 2 or 3 digits.  Is there a way to use your function (other than a switch or if/else chain based on the value of d) that would achieve this?  I tried a very naive making r a function of d as here where n was intended to be the approx digits of precision but it failed horribly:
switch n {
case 1:
r = d / 10
case 2:
r = d / 100
case 3:
r = d / 1000
case 4:
r = d / 10000
case 5:
r = d / 100000
case 6:
r = d / 10000000
}

Because it was kind of fun, here's a significant figure rounding function: http://play.golang.org/p/WjfKwhhjL5. I cheated a bit with math.Pow10. If you want anything more custom than that (e.g. "3.22h", "15.7m", etc.), you'll need to write your own string conversion function. In which case, this would be a good place to start.

 
Thanks to you and all the others for your suggestions,
Gary
Here's a rounding function: http://play.golang.org/p/QHocTHl8iR
Reply all
Reply to author
Forward
Message has been deleted
0 new messages