-
Notifications
You must be signed in to change notification settings - Fork 159
/
duration_string.go
153 lines (137 loc) · 3.35 KB
/
duration_string.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright (C) 2017 ScyllaDB
// Copyright 2010 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.
// This file contains a modified version of time.Duration String function from
// go stdlib src/time/duration.go file together with all required helper functions.
package duration
import "time"
// String is a modified time.Duration String function that supports printing
// days with d suffix, note that day is equal to 24h. This is not supported by
// time.Duration because of issues with leap year and different time zones. We
// decide to ignore that as we are working with UTC.
//
// The implementation is changed not to print units if value is 0 to keep the
// representation shorter i.e. instead of 7d0h0m0s we print just 7d.
//
// 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(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
if u%60 != 0 || w != len(buf)-1 {
w = fmtInt(buf[:w], u%60)
} else {
w = len(buf)
}
u /= 60
// u is now integer minutes
if u > 0 {
if u%60 != 0 {
w--
buf[w] = 'm'
w = fmtInt(buf[:w], u%60)
}
u /= 60
// u is now integer hours
if u > 0 {
if u%24 != 0 {
w--
buf[w] = 'h'
w = fmtInt(buf[:w], u%24)
}
u /= 24
// u is now integer days
if u > 0 {
w--
buf[w] = 'd'
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
}