patch time: Duration, Timer
This commit is contained in:
19
_demo/_timeout/timer.go
Normal file
19
_demo/_timeout/timer.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var c chan int
|
||||
|
||||
func handle(int) {}
|
||||
|
||||
func main() {
|
||||
select {
|
||||
case m := <-c:
|
||||
handle(m)
|
||||
case <-time.After(10 * time.Second):
|
||||
fmt.Println("timed out")
|
||||
}
|
||||
}
|
||||
11
_demo/timedur/timedur.go
Normal file
11
_demo/timedur/timedur.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t := time.Now().Add(time.Second * 5)
|
||||
fmt.Println(time.Until(t))
|
||||
}
|
||||
176
internal/lib/sync/atomic/value.go
Normal file
176
internal/lib/sync/atomic/value.go
Normal file
@@ -0,0 +1,176 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Value struct {
|
||||
v any
|
||||
}
|
||||
|
||||
// efaceWords is interface{} internal representation.
|
||||
type efaceWords struct {
|
||||
typ unsafe.Pointer
|
||||
data unsafe.Pointer
|
||||
}
|
||||
|
||||
// Load returns the value set by the most recent Store.
|
||||
// It returns nil if there has been no call to Store for this Value.
|
||||
func (v *Value) Load() (val any) {
|
||||
vp := (*efaceWords)(unsafe.Pointer(v))
|
||||
typ := LoadPointer(&vp.typ)
|
||||
if typ == nil || typ == unsafe.Pointer(&firstStoreInProgress) {
|
||||
// First store not yet completed.
|
||||
return nil
|
||||
}
|
||||
data := LoadPointer(&vp.data)
|
||||
vlp := (*efaceWords)(unsafe.Pointer(&val))
|
||||
vlp.typ = typ
|
||||
vlp.data = data
|
||||
return
|
||||
}
|
||||
|
||||
var firstStoreInProgress byte
|
||||
|
||||
// Store sets the value of the Value v to val.
|
||||
// All calls to Store for a given Value must use values of the same concrete type.
|
||||
// Store of an inconsistent type panics, as does Store(nil).
|
||||
func (v *Value) Store(val any) {
|
||||
if val == nil {
|
||||
panic("sync/atomic: store of nil value into Value")
|
||||
}
|
||||
vp := (*efaceWords)(unsafe.Pointer(v))
|
||||
vlp := (*efaceWords)(unsafe.Pointer(&val))
|
||||
for {
|
||||
typ := LoadPointer(&vp.typ)
|
||||
if typ == nil {
|
||||
// Attempt to start first store.
|
||||
// Disable preemption so that other goroutines can use
|
||||
// active spin wait to wait for completion.
|
||||
if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) {
|
||||
continue
|
||||
}
|
||||
// Complete first store.
|
||||
StorePointer(&vp.data, vlp.data)
|
||||
StorePointer(&vp.typ, vlp.typ)
|
||||
return
|
||||
}
|
||||
if typ == unsafe.Pointer(&firstStoreInProgress) {
|
||||
// First store in progress. Wait.
|
||||
// Since we disable preemption around the first store,
|
||||
// we can wait with active spinning.
|
||||
continue
|
||||
}
|
||||
// First store completed. Check type and overwrite data.
|
||||
if typ != vlp.typ {
|
||||
panic("sync/atomic: store of inconsistently typed value into Value")
|
||||
}
|
||||
StorePointer(&vp.data, vlp.data)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Swap stores new into Value and returns the previous value. It returns nil if
|
||||
// the Value is empty.
|
||||
//
|
||||
// All calls to Swap for a given Value must use values of the same concrete
|
||||
// type. Swap of an inconsistent type panics, as does Swap(nil).
|
||||
func (v *Value) Swap(new any) (old any) {
|
||||
if new == nil {
|
||||
panic("sync/atomic: swap of nil value into Value")
|
||||
}
|
||||
vp := (*efaceWords)(unsafe.Pointer(v))
|
||||
np := (*efaceWords)(unsafe.Pointer(&new))
|
||||
for {
|
||||
typ := LoadPointer(&vp.typ)
|
||||
if typ == nil {
|
||||
// Attempt to start first store.
|
||||
// Disable preemption so that other goroutines can use
|
||||
// active spin wait to wait for completion; and so that
|
||||
// GC does not see the fake type accidentally.
|
||||
if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) {
|
||||
continue
|
||||
}
|
||||
// Complete first store.
|
||||
StorePointer(&vp.data, np.data)
|
||||
StorePointer(&vp.typ, np.typ)
|
||||
return nil
|
||||
}
|
||||
if typ == unsafe.Pointer(&firstStoreInProgress) {
|
||||
// First store in progress. Wait.
|
||||
// Since we disable preemption around the first store,
|
||||
// we can wait with active spinning.
|
||||
continue
|
||||
}
|
||||
// First store completed. Check type and overwrite data.
|
||||
if typ != np.typ {
|
||||
panic("sync/atomic: swap of inconsistently typed value into Value")
|
||||
}
|
||||
op := (*efaceWords)(unsafe.Pointer(&old))
|
||||
op.typ, op.data = np.typ, SwapPointer(&vp.data, np.data)
|
||||
return old
|
||||
}
|
||||
}
|
||||
|
||||
// CompareAndSwap executes the compare-and-swap operation for the Value.
|
||||
//
|
||||
// All calls to CompareAndSwap for a given Value must use values of the same
|
||||
// concrete type. CompareAndSwap of an inconsistent type panics, as does
|
||||
// CompareAndSwap(old, nil).
|
||||
func (v *Value) CompareAndSwap(old, new any) (swapped bool) {
|
||||
if new == nil {
|
||||
panic("sync/atomic: compare and swap of nil value into Value")
|
||||
}
|
||||
vp := (*efaceWords)(unsafe.Pointer(v))
|
||||
np := (*efaceWords)(unsafe.Pointer(&new))
|
||||
op := (*efaceWords)(unsafe.Pointer(&old))
|
||||
if op.typ != nil && np.typ != op.typ {
|
||||
panic("sync/atomic: compare and swap of inconsistently typed values")
|
||||
}
|
||||
for {
|
||||
typ := LoadPointer(&vp.typ)
|
||||
if typ == nil {
|
||||
if old != nil {
|
||||
return false
|
||||
}
|
||||
// Attempt to start first store.
|
||||
// Disable preemption so that other goroutines can use
|
||||
// active spin wait to wait for completion; and so that
|
||||
// GC does not see the fake type accidentally.
|
||||
if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) {
|
||||
continue
|
||||
}
|
||||
// Complete first store.
|
||||
StorePointer(&vp.data, np.data)
|
||||
StorePointer(&vp.typ, np.typ)
|
||||
return true
|
||||
}
|
||||
if typ == unsafe.Pointer(&firstStoreInProgress) {
|
||||
// First store in progress. Wait.
|
||||
// Since we disable preemption around the first store,
|
||||
// we can wait with active spinning.
|
||||
continue
|
||||
}
|
||||
// First store completed. Check type and overwrite data.
|
||||
if typ != np.typ {
|
||||
panic("sync/atomic: compare and swap of inconsistently typed value into Value")
|
||||
}
|
||||
// Compare old and current via runtime equality check.
|
||||
// This allows value types to be compared, something
|
||||
// not offered by the package functions.
|
||||
// CompareAndSwapPointer below only ensures vp.data
|
||||
// has not changed since LoadPointer.
|
||||
data := LoadPointer(&vp.data)
|
||||
var i any
|
||||
(*efaceWords)(unsafe.Pointer(&i)).typ = typ
|
||||
(*efaceWords)(unsafe.Pointer(&i)).data = data
|
||||
if i != old {
|
||||
return false
|
||||
}
|
||||
return CompareAndSwapPointer(&vp.data, data, np.data)
|
||||
}
|
||||
}
|
||||
184
internal/lib/time/sleep.go
Normal file
184
internal/lib/time/sleep.go
Normal file
@@ -0,0 +1,184 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package time
|
||||
|
||||
// Sleep pauses the current goroutine for at least the duration d.
|
||||
// A negative or zero duration causes Sleep to return immediately.
|
||||
func Sleep(d Duration) {
|
||||
panic("todo: time.Sleep")
|
||||
}
|
||||
|
||||
// Interface to timers implemented in package runtime.
|
||||
// Must be in sync with ../runtime/time.go:/^type timer
|
||||
type runtimeTimer struct {
|
||||
pp uintptr
|
||||
when int64
|
||||
period int64
|
||||
f func(any, uintptr) // NOTE: must not be closure
|
||||
arg any
|
||||
seq uintptr
|
||||
nextwhen int64
|
||||
status uint32
|
||||
}
|
||||
|
||||
// when is a helper function for setting the 'when' field of a runtimeTimer.
|
||||
// It returns what the time will be, in nanoseconds, Duration d in the future.
|
||||
// If d is negative, it is ignored. If the returned value would be less than
|
||||
// zero because of an overflow, MaxInt64 is returned.
|
||||
func when(d Duration) int64 {
|
||||
if d <= 0 {
|
||||
return runtimeNano()
|
||||
}
|
||||
t := runtimeNano() + int64(d)
|
||||
if t < 0 {
|
||||
// N.B. runtimeNano() and d are always positive, so addition
|
||||
// (including overflow) will never result in t == 0.
|
||||
t = 1<<63 - 1 // math.MaxInt64
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func startTimer(*runtimeTimer) { panic("todo: time.startTimer") }
|
||||
func stopTimer(*runtimeTimer) bool { panic("todo: time.stopTimer") }
|
||||
func resetTimer(*runtimeTimer, int64) bool { panic("todo: time.resetTimer") }
|
||||
|
||||
/* TODO(xsw):
|
||||
func modTimer(t *runtimeTimer, when, period int64, f func(any, uintptr), arg any, seq uintptr) {
|
||||
panic("todo: time.modTimer")
|
||||
}
|
||||
*/
|
||||
|
||||
// The Timer type represents a single event.
|
||||
// When the Timer expires, the current time will be sent on C,
|
||||
// unless the Timer was created by AfterFunc.
|
||||
// A Timer must be created with NewTimer or AfterFunc.
|
||||
type Timer struct {
|
||||
C <-chan Time
|
||||
r runtimeTimer
|
||||
}
|
||||
|
||||
// Stop prevents the Timer from firing.
|
||||
// It returns true if the call stops the timer, false if the timer has already
|
||||
// expired or been stopped.
|
||||
// Stop does not close the channel, to prevent a read from the channel succeeding
|
||||
// incorrectly.
|
||||
//
|
||||
// To ensure the channel is empty after a call to Stop, check the
|
||||
// return value and drain the channel.
|
||||
// For example, assuming the program has not received from t.C already:
|
||||
//
|
||||
// if !t.Stop() {
|
||||
// <-t.C
|
||||
// }
|
||||
//
|
||||
// This cannot be done concurrent to other receives from the Timer's
|
||||
// channel or other calls to the Timer's Stop method.
|
||||
//
|
||||
// For a timer created with AfterFunc(d, f), if t.Stop returns false, then the timer
|
||||
// has already expired and the function f has been started in its own goroutine;
|
||||
// Stop does not wait for f to complete before returning.
|
||||
// If the caller needs to know whether f is completed, it must coordinate
|
||||
// with f explicitly.
|
||||
func (t *Timer) Stop() bool {
|
||||
if t.r.f == nil {
|
||||
panic("time: Stop called on uninitialized Timer")
|
||||
}
|
||||
return stopTimer(&t.r)
|
||||
}
|
||||
|
||||
// NewTimer creates a new Timer that will send
|
||||
// the current time on its channel after at least duration d.
|
||||
func NewTimer(d Duration) *Timer {
|
||||
c := make(chan Time, 1)
|
||||
t := &Timer{
|
||||
C: c,
|
||||
r: runtimeTimer{
|
||||
when: when(d),
|
||||
f: sendTime,
|
||||
arg: c,
|
||||
},
|
||||
}
|
||||
startTimer(&t.r)
|
||||
return t
|
||||
}
|
||||
|
||||
// Reset changes the timer to expire after duration d.
|
||||
// It returns true if the timer had been active, false if the timer had
|
||||
// expired or been stopped.
|
||||
//
|
||||
// For a Timer created with NewTimer, Reset should be invoked only on
|
||||
// stopped or expired timers with drained channels.
|
||||
//
|
||||
// If a program has already received a value from t.C, the timer is known
|
||||
// to have expired and the channel drained, so t.Reset can be used directly.
|
||||
// If a program has not yet received a value from t.C, however,
|
||||
// the timer must be stopped and—if Stop reports that the timer expired
|
||||
// before being stopped—the channel explicitly drained:
|
||||
//
|
||||
// if !t.Stop() {
|
||||
// <-t.C
|
||||
// }
|
||||
// t.Reset(d)
|
||||
//
|
||||
// This should not be done concurrent to other receives from the Timer's
|
||||
// channel.
|
||||
//
|
||||
// Note that it is not possible to use Reset's return value correctly, as there
|
||||
// is a race condition between draining the channel and the new timer expiring.
|
||||
// Reset should always be invoked on stopped or expired channels, as described above.
|
||||
// The return value exists to preserve compatibility with existing programs.
|
||||
//
|
||||
// For a Timer created with AfterFunc(d, f), Reset either reschedules
|
||||
// when f will run, in which case Reset returns true, or schedules f
|
||||
// to run again, in which case it returns false.
|
||||
// When Reset returns false, Reset neither waits for the prior f to
|
||||
// complete before returning nor does it guarantee that the subsequent
|
||||
// goroutine running f does not run concurrently with the prior
|
||||
// one. If the caller needs to know whether the prior execution of
|
||||
// f is completed, it must coordinate with f explicitly.
|
||||
func (t *Timer) Reset(d Duration) bool {
|
||||
if t.r.f == nil {
|
||||
panic("time: Reset called on uninitialized Timer")
|
||||
}
|
||||
w := when(d)
|
||||
return resetTimer(&t.r, w)
|
||||
}
|
||||
|
||||
// sendTime does a non-blocking send of the current time on c.
|
||||
func sendTime(c any, seq uintptr) {
|
||||
select {
|
||||
case c.(chan Time) <- Now():
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// After waits for the duration to elapse and then sends the current time
|
||||
// on the returned channel.
|
||||
// It is equivalent to NewTimer(d).C.
|
||||
// The underlying Timer is not recovered by the garbage collector
|
||||
// until the timer fires. If efficiency is a concern, use NewTimer
|
||||
// instead and call Timer.Stop if the timer is no longer needed.
|
||||
func After(d Duration) <-chan Time {
|
||||
return NewTimer(d).C
|
||||
}
|
||||
|
||||
// AfterFunc waits for the duration to elapse and then calls f
|
||||
// in its own goroutine. It returns a Timer that can
|
||||
// be used to cancel the call using its Stop method.
|
||||
func AfterFunc(d Duration, f func()) *Timer {
|
||||
t := &Timer{
|
||||
r: runtimeTimer{
|
||||
when: when(d),
|
||||
f: goFunc,
|
||||
arg: f,
|
||||
},
|
||||
}
|
||||
startTimer(&t.r)
|
||||
return t
|
||||
}
|
||||
|
||||
func goFunc(arg any, seq uintptr) {
|
||||
go arg.(func())()
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package time
|
||||
// llgo:skipall
|
||||
import (
|
||||
"unsafe"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/goplus/llgo/c"
|
||||
"github.com/goplus/llgo/c/time"
|
||||
@@ -620,6 +619,332 @@ func (t Time) YearDay() int {
|
||||
return yday + 1
|
||||
}
|
||||
|
||||
// A Duration represents the elapsed time between two instants
|
||||
// as an int64 nanosecond count. The representation limits the
|
||||
// largest representable duration to approximately 290 years.
|
||||
type Duration int64
|
||||
|
||||
const (
|
||||
minDuration Duration = -1 << 63
|
||||
maxDuration Duration = 1<<63 - 1
|
||||
)
|
||||
|
||||
// Common durations. There is no definition for units of Day or larger
|
||||
// to avoid confusion across daylight savings time zone transitions.
|
||||
//
|
||||
// To count the number of units in a Duration, divide:
|
||||
//
|
||||
// second := time.Second
|
||||
// fmt.Print(int64(second/time.Millisecond)) // prints 1000
|
||||
//
|
||||
// To convert an integer number of units to a Duration, multiply:
|
||||
//
|
||||
// seconds := 10
|
||||
// fmt.Print(time.Duration(seconds)*time.Second) // prints 10s
|
||||
const (
|
||||
Nanosecond Duration = 1
|
||||
Microsecond = 1000 * Nanosecond
|
||||
Millisecond = 1000 * Microsecond
|
||||
Second = 1000 * Millisecond
|
||||
Minute = 60 * Second
|
||||
Hour = 60 * Minute
|
||||
)
|
||||
|
||||
// String returns a string representing the duration in the form "72h3m0.5s".
|
||||
// Leading zero units are omitted. As a special case, durations less than one
|
||||
// second format use a smaller unit (milli-, micro-, or nanoseconds) to ensure
|
||||
// that the leading digit is non-zero. The zero duration formats as 0s.
|
||||
func (d Duration) String() string {
|
||||
// Largest time is 2540400h10m10.000000000s
|
||||
var buf [32]byte
|
||||
w := len(buf)
|
||||
|
||||
u := uint64(d)
|
||||
neg := d < 0
|
||||
if neg {
|
||||
u = -u
|
||||
}
|
||||
|
||||
if u < uint64(Second) {
|
||||
// Special case: if duration is smaller than a second,
|
||||
// use smaller units, like 1.2ms
|
||||
var prec int
|
||||
w--
|
||||
buf[w] = 's'
|
||||
w--
|
||||
switch {
|
||||
case u == 0:
|
||||
return "0s"
|
||||
case u < uint64(Microsecond):
|
||||
// print nanoseconds
|
||||
prec = 0
|
||||
buf[w] = 'n'
|
||||
case u < uint64(Millisecond):
|
||||
// print microseconds
|
||||
prec = 3
|
||||
// U+00B5 'µ' micro sign == 0xC2 0xB5
|
||||
w-- // Need room for two bytes.
|
||||
copy(buf[w:], "µ")
|
||||
default:
|
||||
// print milliseconds
|
||||
prec = 6
|
||||
buf[w] = 'm'
|
||||
}
|
||||
w, u = fmtFrac(buf[:w], u, prec)
|
||||
w = fmtInt(buf[:w], u)
|
||||
} else {
|
||||
w--
|
||||
buf[w] = 's'
|
||||
|
||||
w, u = fmtFrac(buf[:w], u, 9)
|
||||
|
||||
// u is now integer seconds
|
||||
w = fmtInt(buf[:w], u%60)
|
||||
u /= 60
|
||||
|
||||
// u is now integer minutes
|
||||
if u > 0 {
|
||||
w--
|
||||
buf[w] = 'm'
|
||||
w = fmtInt(buf[:w], u%60)
|
||||
u /= 60
|
||||
|
||||
// u is now integer hours
|
||||
// Stop at hours because days can be different lengths.
|
||||
if u > 0 {
|
||||
w--
|
||||
buf[w] = 'h'
|
||||
w = fmtInt(buf[:w], u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if neg {
|
||||
w--
|
||||
buf[w] = '-'
|
||||
}
|
||||
|
||||
return string(buf[w:])
|
||||
}
|
||||
|
||||
// fmtFrac formats the fraction of v/10**prec (e.g., ".12345") into the
|
||||
// tail of buf, omitting trailing zeros. It omits the decimal
|
||||
// point too when the fraction is 0. It returns the index where the
|
||||
// output bytes begin and the value v/10**prec.
|
||||
func fmtFrac(buf []byte, v uint64, prec int) (nw int, nv uint64) {
|
||||
// Omit trailing zeros up to and including decimal point.
|
||||
w := len(buf)
|
||||
print := false
|
||||
for i := 0; i < prec; i++ {
|
||||
digit := v % 10
|
||||
print = print || digit != 0
|
||||
if print {
|
||||
w--
|
||||
buf[w] = byte(digit) + '0'
|
||||
}
|
||||
v /= 10
|
||||
}
|
||||
if print {
|
||||
w--
|
||||
buf[w] = '.'
|
||||
}
|
||||
return w, v
|
||||
}
|
||||
|
||||
// fmtInt formats v into the tail of buf.
|
||||
// It returns the index where the output begins.
|
||||
func fmtInt(buf []byte, v uint64) int {
|
||||
w := len(buf)
|
||||
if v == 0 {
|
||||
w--
|
||||
buf[w] = '0'
|
||||
} else {
|
||||
for v > 0 {
|
||||
w--
|
||||
buf[w] = byte(v%10) + '0'
|
||||
v /= 10
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// Nanoseconds returns the duration as an integer nanosecond count.
|
||||
func (d Duration) Nanoseconds() int64 { return int64(d) }
|
||||
|
||||
// Microseconds returns the duration as an integer microsecond count.
|
||||
func (d Duration) Microseconds() int64 { return int64(d) / 1e3 }
|
||||
|
||||
// Milliseconds returns the duration as an integer millisecond count.
|
||||
func (d Duration) Milliseconds() int64 { return int64(d) / 1e6 }
|
||||
|
||||
// These methods return float64 because the dominant
|
||||
// use case is for printing a floating point number like 1.5s, and
|
||||
// a truncation to integer would make them not useful in those cases.
|
||||
// Splitting the integer and fraction ourselves guarantees that
|
||||
// converting the returned float64 to an integer rounds the same
|
||||
// way that a pure integer conversion would have, even in cases
|
||||
// where, say, float64(d.Nanoseconds())/1e9 would have rounded
|
||||
// differently.
|
||||
|
||||
// Seconds returns the duration as a floating point number of seconds.
|
||||
func (d Duration) Seconds() float64 {
|
||||
sec := d / Second
|
||||
nsec := d % Second
|
||||
return float64(sec) + float64(nsec)/1e9
|
||||
}
|
||||
|
||||
// Minutes returns the duration as a floating point number of minutes.
|
||||
func (d Duration) Minutes() float64 {
|
||||
min := d / Minute
|
||||
nsec := d % Minute
|
||||
return float64(min) + float64(nsec)/(60*1e9)
|
||||
}
|
||||
|
||||
// Hours returns the duration as a floating point number of hours.
|
||||
func (d Duration) Hours() float64 {
|
||||
hour := d / Hour
|
||||
nsec := d % Hour
|
||||
return float64(hour) + float64(nsec)/(60*60*1e9)
|
||||
}
|
||||
|
||||
// Truncate returns the result of rounding d toward zero to a multiple of m.
|
||||
// If m <= 0, Truncate returns d unchanged.
|
||||
func (d Duration) Truncate(m Duration) Duration {
|
||||
if m <= 0 {
|
||||
return d
|
||||
}
|
||||
return d - d%m
|
||||
}
|
||||
|
||||
// lessThanHalf reports whether x+x < y but avoids overflow,
|
||||
// assuming x and y are both positive (Duration is signed).
|
||||
func lessThanHalf(x, y Duration) bool {
|
||||
return uint64(x)+uint64(x) < uint64(y)
|
||||
}
|
||||
|
||||
// Round returns the result of rounding d to the nearest multiple of m.
|
||||
// The rounding behavior for halfway values is to round away from zero.
|
||||
// If the result exceeds the maximum (or minimum)
|
||||
// value that can be stored in a Duration,
|
||||
// Round returns the maximum (or minimum) duration.
|
||||
// If m <= 0, Round returns d unchanged.
|
||||
func (d Duration) Round(m Duration) Duration {
|
||||
if m <= 0 {
|
||||
return d
|
||||
}
|
||||
r := d % m
|
||||
if d < 0 {
|
||||
r = -r
|
||||
if lessThanHalf(r, m) {
|
||||
return d + r
|
||||
}
|
||||
if d1 := d - m + r; d1 < d {
|
||||
return d1
|
||||
}
|
||||
return minDuration // overflow
|
||||
}
|
||||
if lessThanHalf(r, m) {
|
||||
return d - r
|
||||
}
|
||||
if d1 := d + m - r; d1 > d {
|
||||
return d1
|
||||
}
|
||||
return maxDuration // overflow
|
||||
}
|
||||
|
||||
// Abs returns the absolute value of d.
|
||||
// As a special case, math.MinInt64 is converted to math.MaxInt64.
|
||||
func (d Duration) Abs() Duration {
|
||||
switch {
|
||||
case d >= 0:
|
||||
return d
|
||||
case d == minDuration:
|
||||
return maxDuration
|
||||
default:
|
||||
return -d
|
||||
}
|
||||
}
|
||||
|
||||
// Add returns the time t+d.
|
||||
func (t Time) Add(d Duration) Time {
|
||||
dsec := int64(d / 1e9)
|
||||
nsec := t.nsec() + int32(d%1e9)
|
||||
if nsec >= 1e9 {
|
||||
dsec++
|
||||
nsec -= 1e9
|
||||
} else if nsec < 0 {
|
||||
dsec--
|
||||
nsec += 1e9
|
||||
}
|
||||
t.wall = t.wall&^nsecMask | uint64(nsec) // update nsec
|
||||
t.addSec(dsec)
|
||||
if t.wall&hasMonotonic != 0 {
|
||||
te := t.ext + int64(d)
|
||||
if d < 0 && te > t.ext || d > 0 && te < t.ext {
|
||||
// Monotonic clock reading now out of range; degrade to wall-only.
|
||||
t.stripMono()
|
||||
} else {
|
||||
t.ext = te
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Sub returns the duration t-u. If the result exceeds the maximum (or minimum)
|
||||
// value that can be stored in a Duration, the maximum (or minimum) duration
|
||||
// will be returned.
|
||||
// To compute t-d for a duration d, use t.Add(-d).
|
||||
func (t Time) Sub(u Time) Duration {
|
||||
if t.wall&u.wall&hasMonotonic != 0 {
|
||||
te := t.ext
|
||||
ue := u.ext
|
||||
d := Duration(te - ue)
|
||||
if d < 0 && te > ue {
|
||||
return maxDuration // t - u is positive out of range
|
||||
}
|
||||
if d > 0 && te < ue {
|
||||
return minDuration // t - u is negative out of range
|
||||
}
|
||||
return d
|
||||
}
|
||||
d := Duration(t.sec()-u.sec())*Second + Duration(t.nsec()-u.nsec())
|
||||
// Check for overflow or underflow.
|
||||
switch {
|
||||
case u.Add(d).Equal(t):
|
||||
return d // d is correct
|
||||
case t.Before(u):
|
||||
return minDuration // t - u is negative out of range
|
||||
default:
|
||||
return maxDuration // t - u is positive out of range
|
||||
}
|
||||
}
|
||||
|
||||
// Since returns the time elapsed since t.
|
||||
// It is shorthand for time.Now().Sub(t).
|
||||
func Since(t Time) Duration {
|
||||
var now Time
|
||||
if t.wall&hasMonotonic != 0 {
|
||||
// Common case optimization: if t has monotonic time, then Sub will use only it.
|
||||
now = Time{hasMonotonic, runtimeNano() - startNano, nil}
|
||||
} else {
|
||||
now = Now()
|
||||
}
|
||||
return now.Sub(t)
|
||||
}
|
||||
|
||||
// Until returns the duration until t.
|
||||
// It is shorthand for t.Sub(time.Now()).
|
||||
func Until(t Time) Duration {
|
||||
var now Time
|
||||
if t.wall&hasMonotonic != 0 {
|
||||
// Common case optimization: if t has monotonic time, then Sub will use only it.
|
||||
now = Time{hasMonotonic, runtimeNano() - startNano, nil}
|
||||
} else {
|
||||
now = Now()
|
||||
}
|
||||
return t.Sub(now)
|
||||
}
|
||||
|
||||
// Date returns the Time corresponding to
|
||||
//
|
||||
// yyyy-mm-dd hh:mm:ss + nsec nanoseconds
|
||||
@@ -717,20 +1042,3 @@ func norm(hi, lo, base int) (nhi, nlo int) {
|
||||
}
|
||||
return hi, lo
|
||||
}
|
||||
|
||||
// fmtInt formats v into the tail of buf.
|
||||
// It returns the index where the output begins.
|
||||
func fmtInt(buf []byte, v uint64) int {
|
||||
w := len(buf)
|
||||
if v == 0 {
|
||||
w--
|
||||
buf[w] = '0'
|
||||
} else {
|
||||
for v > 0 {
|
||||
w--
|
||||
buf[w] = byte(v%10) + '0'
|
||||
v /= 10
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user