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
|
// llgo:skipall
|
||||||
import (
|
import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
_ "unsafe"
|
|
||||||
|
|
||||||
"github.com/goplus/llgo/c"
|
"github.com/goplus/llgo/c"
|
||||||
"github.com/goplus/llgo/c/time"
|
"github.com/goplus/llgo/c/time"
|
||||||
@@ -620,6 +619,332 @@ func (t Time) YearDay() int {
|
|||||||
return yday + 1
|
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
|
// Date returns the Time corresponding to
|
||||||
//
|
//
|
||||||
// yyyy-mm-dd hh:mm:ss + nsec nanoseconds
|
// yyyy-mm-dd hh:mm:ss + nsec nanoseconds
|
||||||
@@ -717,20 +1042,3 @@ func norm(hi, lo, base int) (nhi, nlo int) {
|
|||||||
}
|
}
|
||||||
return hi, lo
|
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