Merge pull request #762 from spongehah/golib/time
lib/time: patch div and Time.Round
This commit is contained in:
@@ -1072,3 +1072,114 @@ func norm(hi, lo, base int) (nhi, nlo int) {
|
||||
}
|
||||
return hi, lo
|
||||
}
|
||||
|
||||
// Round returns the result of rounding t to the nearest multiple of d (since the zero time).
|
||||
// The rounding behavior for halfway values is to round up.
|
||||
// If d <= 0, Round returns t stripped of any monotonic clock reading but otherwise unchanged.
|
||||
//
|
||||
// Round operates on the time as an absolute duration since the
|
||||
// zero time; it does not operate on the presentation form of the
|
||||
// time. Thus, Round(Hour) may return a time with a non-zero
|
||||
// minute, depending on the time's Location.
|
||||
func (t Time) Round(d Duration) Time {
|
||||
t.stripMono()
|
||||
if d <= 0 {
|
||||
return t
|
||||
}
|
||||
_, r := div(t, d)
|
||||
if lessThanHalf(r, d) {
|
||||
return t.Add(-r)
|
||||
}
|
||||
return t.Add(d - r)
|
||||
}
|
||||
|
||||
// div divides t by d and returns the quotient parity and remainder.
|
||||
// We don't use the quotient parity anymore (round half up instead of round to even)
|
||||
// but it's still here in case we change our minds.
|
||||
func div(t Time, d Duration) (qmod2 int, r Duration) {
|
||||
neg := false
|
||||
nsec := t.nsec()
|
||||
sec := t.sec()
|
||||
if sec < 0 {
|
||||
// Operate on absolute value.
|
||||
neg = true
|
||||
sec = -sec
|
||||
nsec = -nsec
|
||||
if nsec < 0 {
|
||||
nsec += 1e9
|
||||
sec-- // sec >= 1 before the -- so safe
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
// Special case: 2d divides 1 second.
|
||||
case d < Second && Second%(d+d) == 0:
|
||||
qmod2 = int(nsec/int32(d)) & 1
|
||||
r = Duration(nsec % int32(d))
|
||||
|
||||
// Special case: d is a multiple of 1 second.
|
||||
case d%Second == 0:
|
||||
d1 := int64(d / Second)
|
||||
qmod2 = int(sec/d1) & 1
|
||||
r = Duration(sec%d1)*Second + Duration(nsec)
|
||||
|
||||
// General case.
|
||||
// This could be faster if more cleverness were applied,
|
||||
// but it's really only here to avoid special case restrictions in the API.
|
||||
// No one will care about these cases.
|
||||
default:
|
||||
// Compute nanoseconds as 128-bit number.
|
||||
sec := uint64(sec)
|
||||
tmp := (sec >> 32) * 1e9
|
||||
u1 := tmp >> 32
|
||||
u0 := tmp << 32
|
||||
tmp = (sec & 0xFFFFFFFF) * 1e9
|
||||
u0x, u0 := u0, u0+tmp
|
||||
if u0 < u0x {
|
||||
u1++
|
||||
}
|
||||
u0x, u0 = u0, u0+uint64(nsec)
|
||||
if u0 < u0x {
|
||||
u1++
|
||||
}
|
||||
|
||||
// Compute remainder by subtracting r<<k for decreasing k.
|
||||
// Quotient parity is whether we subtract on last round.
|
||||
d1 := uint64(d)
|
||||
for d1>>63 != 1 {
|
||||
d1 <<= 1
|
||||
}
|
||||
d0 := uint64(0)
|
||||
for {
|
||||
qmod2 = 0
|
||||
if u1 > d1 || u1 == d1 && u0 >= d0 {
|
||||
// subtract
|
||||
qmod2 = 1
|
||||
u0x, u0 = u0, u0-d0
|
||||
if u0 > u0x {
|
||||
u1--
|
||||
}
|
||||
u1 -= d1
|
||||
}
|
||||
if d1 == 0 && d0 == uint64(d) {
|
||||
break
|
||||
}
|
||||
d0 >>= 1
|
||||
d0 |= (d1 & 1) << 63
|
||||
d1 >>= 1
|
||||
}
|
||||
r = Duration(u0)
|
||||
}
|
||||
|
||||
if neg && r != 0 {
|
||||
// If input was negative and not an exact multiple of d, we computed q, r such that
|
||||
// q*d + r = -t
|
||||
// But the right answers are given by -(q-1), d-r:
|
||||
// q*d + r = -t
|
||||
// -q*d - r = t
|
||||
// -(q-1)*d + (d - r) = t
|
||||
qmod2 ^= 1
|
||||
r = d - r
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user