package humanize import ( "bytes" "git.nspix.com/golang/kos/util/bs" "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) }