2023-08-07 14:57:16 +08:00
|
|
|
package humanize
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2024-11-12 17:47:28 +08:00
|
|
|
"git.nobla.cn/golang/kos/util/bs"
|
2023-08-07 14:57:16 +08:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
Nanosecond Duration = 1
|
|
|
|
Microsecond = 1000 * Nanosecond
|
|
|
|
Millisecond = 1000 * Microsecond
|
|
|
|
Second = 1000 * Millisecond
|
|
|
|
Minute = 60 * Second
|
|
|
|
Hour = 60 * Minute
|
|
|
|
)
|
|
|
|
|
|
|
|
type Duration int64
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
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(time.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(time.Microsecond):
|
|
|
|
// print nanoseconds
|
|
|
|
prec = 0
|
|
|
|
buf[w] = 'n'
|
|
|
|
case u < uint64(time.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:])
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *Duration) UnmarshalJSON(b []byte) (err error) {
|
|
|
|
var n time.Duration
|
|
|
|
b = bytes.TrimFunc(b, func(r rune) bool {
|
|
|
|
if r == '"' {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
if n, err = time.ParseDuration(bs.BytesToString(b)); err == nil {
|
|
|
|
*d = Duration(n)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d Duration) MarshalJSON() ([]byte, error) {
|
|
|
|
return bs.StringToBytes(`"` + d.String() + `"`), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d Duration) Duration() time.Duration {
|
|
|
|
return time.Duration(d)
|
|
|
|
}
|