Skip to content

Commit 017325b

Browse files
authored
Merge pull request #3 from xhit/2-proposal-add-string-function-that-prints-out-days-and-weeks
Convert duration to string supporting days and weeks
2 parents 741dffc + 37c349f commit 017325b

File tree

3 files changed

+210
-1
lines changed

3 files changed

+210
-1
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,5 @@ func main() {
8484
}
8585
}
8686
```
87+
88+
Also, you can convert to string the duration using `String(t time.Duration)` function. This support weeks and days and not return the ugly decimals from golang standard `t.String()` function. Units with 0 values aren't returned. For example: `1d1ms` means 1 day 1 millisecond.

str2duration.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,150 @@ func leadingFraction(s string) (x int64, scale float64, rem string) {
182182
}
183183
return x, scale, s[i:]
184184
}
185+
186+
// String returns a string representing the duration in the form "1w4d2h3m5s".
187+
// Units with 0 values aren't returned, for example: 1d1ms is 1 day 1 milliseconds
188+
func String(d time.Duration) string {
189+
if d == 0 {
190+
return "0s"
191+
}
192+
193+
// Largest time is 15250w1d23h47m16s854ms775us807ns
194+
var buf [32]byte
195+
w := len(buf)
196+
var sign string
197+
198+
u := uint64(d)
199+
neg := d < 0
200+
if neg {
201+
u = -u
202+
sign = "-"
203+
}
204+
205+
// u is nanoseconds (ns)
206+
if u > 0 {
207+
w--
208+
209+
if u%1000 > 0 {
210+
buf[w] = 's'
211+
w--
212+
buf[w] = 'n'
213+
w = fmtInt(buf[:w], u%1000)
214+
} else {
215+
w++
216+
}
217+
218+
u /= 1000
219+
220+
// u is now integer microseconds (us)
221+
if u > 0 {
222+
w--
223+
if u%1000 > 0 {
224+
buf[w] = 's'
225+
w--
226+
buf[w] = 'u'
227+
w = fmtInt(buf[:w], u%1000)
228+
} else {
229+
w++
230+
}
231+
u /= 1000
232+
233+
// u is now integer milliseconds (ms)
234+
if u > 0 {
235+
w--
236+
if u%1000 > 0 {
237+
buf[w] = 's'
238+
w--
239+
buf[w] = 'm'
240+
w = fmtInt(buf[:w], u%1000)
241+
} else {
242+
w++
243+
}
244+
u /= 1000
245+
246+
// u is now integer seconds (s)
247+
if u > 0 {
248+
w--
249+
if u%60 > 0 {
250+
buf[w] = 's'
251+
w = fmtInt(buf[:w], u%60)
252+
} else {
253+
w++
254+
}
255+
u /= 60
256+
257+
// u is now integer minutes (m)
258+
if u > 0 {
259+
w--
260+
261+
if u%60 > 0 {
262+
buf[w] = 'm'
263+
w = fmtInt(buf[:w], u%60)
264+
} else {
265+
w++
266+
}
267+
268+
u /= 60
269+
270+
// u is now integer hours (h)
271+
if u > 0 {
272+
w--
273+
274+
if u%24 > 0 {
275+
buf[w] = 'h'
276+
w = fmtInt(buf[:w], u%24)
277+
} else {
278+
w++
279+
}
280+
281+
u /= 24
282+
283+
// u is now integer days (d)
284+
if u > 0 {
285+
w--
286+
287+
if u%7 > 0 {
288+
buf[w] = 'd'
289+
w = fmtInt(buf[:w], u%7)
290+
} else {
291+
w++
292+
}
293+
294+
u /= 7
295+
296+
// u is now integer weeks (w)
297+
if u > 0 {
298+
w--
299+
buf[w] = 'w'
300+
w = fmtInt(buf[:w], u)
301+
}
302+
303+
}
304+
305+
}
306+
}
307+
}
308+
}
309+
}
310+
311+
}
312+
313+
return sign + string(buf[w:])
314+
}
315+
316+
// fmtInt formats v into the tail of buf.
317+
// It returns the index where the output begins.
318+
func fmtInt(buf []byte, v uint64) int {
319+
w := len(buf)
320+
if v == 0 {
321+
w--
322+
buf[w] = '0'
323+
} else {
324+
for v > 0 {
325+
w--
326+
buf[w] = byte(v%10) + '0'
327+
v /= 10
328+
}
329+
}
330+
return w
331+
}

str2duration_test.go

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func TestParseString(t *testing.T) {
4949
}
5050
}
5151

52-
//TestParseDuration test if string returned by a duration is equal to string returned with the package
52+
// TestParseDuration test if string returned by a duration is equal to string returned with the package
5353
func TestParseDuration(t *testing.T) {
5454

5555
for i, duration := range []time.Duration{
@@ -89,3 +89,63 @@ func TestParseDuration(t *testing.T) {
8989
}
9090
}
9191
}
92+
93+
func TestString(t *testing.T) {
94+
95+
for i, tt := range []struct {
96+
dur time.Duration
97+
expected string
98+
}{
99+
//This times are returned with time.Duration string
100+
{time.Duration(0), "0s"},
101+
{time.Duration(time.Hour), "1h"},
102+
{time.Duration(time.Minute), "1m"},
103+
{time.Duration(time.Second), "1s"},
104+
{time.Duration(time.Millisecond), "1ms"},
105+
{time.Duration(time.Microsecond), "1us"},
106+
{time.Duration(time.Nanosecond), "1ns"},
107+
{time.Duration(4*time.Second + time.Nanosecond), "4s1ns"},
108+
{time.Duration(time.Hour + 4*time.Second + time.Nanosecond), "1h4s1ns"},
109+
{time.Duration(61*time.Minute + 10*time.Millisecond), "1h1m10ms"},
110+
{time.Duration(61*time.Minute + 123456789*time.Nanosecond), "1h1m123ms456us789ns"},
111+
{time.Duration(time.Millisecond + 20*time.Nanosecond), "1ms20ns"},
112+
{time.Duration(time.Second + 20*time.Nanosecond), "1s20ns"},
113+
{time.Duration(693 * time.Nanosecond), "693ns"},
114+
{time.Duration(10*time.Second + time.Microsecond + 693*time.Nanosecond), "10s1us693ns"},
115+
{time.Duration(time.Millisecond + 1*time.Nanosecond), "1ms1ns"},
116+
{time.Duration(time.Second + 20*time.Nanosecond), "1s20ns"},
117+
{time.Duration(60*time.Hour + 8*time.Millisecond), "2d12h8ms"},
118+
{time.Duration(96*time.Hour + 63*time.Second), "4d1m3s"},
119+
{time.Duration(48*time.Hour + 3*time.Second + 96*time.Nanosecond), "2d3s96ns"},
120+
{time.Duration(168*time.Hour + 48*time.Hour + 3*time.Second + 96*time.Nanosecond), "1w2d3s96ns"},
121+
{time.Duration(168*time.Hour + 48*time.Hour + 3*time.Second + 3*time.Microsecond + 96*time.Nanosecond), "1w2d3s3us96ns"},
122+
123+
// this is the maximum supported by golang std: 2540400h10m10.000000000s
124+
{time.Duration(2540400*time.Hour + 10*time.Minute + 10*time.Second), "15121w3d10m10s"},
125+
126+
// this is max int64, the max duration supported to convert
127+
{time.Duration(9_223_372_036_854_775_807 * time.Nanosecond), "15250w1d23h47m16s854ms775us807ns"},
128+
129+
// this is max int64 in negative, the max negative duration supported to convert
130+
{time.Duration(-9_223_372_036_854_775_807 * time.Nanosecond), "-15250w1d23h47m16s854ms775us807ns"},
131+
132+
// negatives
133+
{time.Duration(-time.Hour), "-1h"},
134+
{time.Duration(-time.Minute), "-1m"},
135+
{time.Duration(-time.Second), "-1s"},
136+
{time.Duration(-time.Millisecond), "-1ms"},
137+
{time.Duration(-time.Microsecond), "-1us"},
138+
{time.Duration(-time.Nanosecond), "-1ns"},
139+
{time.Duration(-4*time.Second - time.Nanosecond), "-4s1ns"},
140+
} {
141+
stringDuration := String(tt.dur)
142+
if tt.expected != stringDuration {
143+
t.Errorf("index %d -> in: %s returned: %s\tnot equal to %s", i, tt.dur, stringDuration, tt.expected)
144+
}
145+
146+
durationParsed, _ := ParseDuration(stringDuration)
147+
if durationParsed != tt.dur {
148+
t.Errorf("error converting string to duration: index %d -> in: %s returned: %s", i, tt.dur, durationParsed)
149+
}
150+
}
151+
}

0 commit comments

Comments
 (0)