pax_global_header 0000666 0000000 0000000 00000000064 14447354234 0014524 g ustar 00root root 0000000 0000000 52 comment=4e55dcca3035f895aa7ac90c6cf3c8c299136411
golang-github-xhit-go-str2duration-2.1.0/ 0000775 0000000 0000000 00000000000 14447354234 0020266 5 ustar 00root root 0000000 0000000 golang-github-xhit-go-str2duration-2.1.0/.github/ 0000775 0000000 0000000 00000000000 14447354234 0021626 5 ustar 00root root 0000000 0000000 golang-github-xhit-go-str2duration-2.1.0/.github/FUNDING.yml 0000664 0000000 0000000 00000001231 14447354234 0023440 0 ustar 00root root 0000000 0000000 # These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://paypal.me/hit']
golang-github-xhit-go-str2duration-2.1.0/LICENSE 0000664 0000000 0000000 00000002706 14447354234 0021300 0 ustar 00root root 0000000 0000000 Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. golang-github-xhit-go-str2duration-2.1.0/README.md 0000664 0000000 0000000 00000007536 14447354234 0021560 0 ustar 00root root 0000000 0000000 # Go String To Duration (go-str2duration)
This package allows to get a time.Duration from a string. The string can be a string retorned for time.Duration or a similar string with weeks or days too!.
## Download
```bash
go get github.com/xhit/go-str2duration/v2
```
## Features
Go String To Duration supports this strings conversions to duration:
- All strings returned in time.Duration String.
- A string more readable like 1w2d6h3ns (1 week 2 days 6 hours and 3 nanoseconds).
- `µs` and `us` are microsecond.
It's the same `time.ParseDuration` standard function in Go, but with days and week support.
**Note**: a day is 24 hour.
If you don't need days and weeks, use [`time.ParseDuration`](https://golang.org/pkg/time/#ParseDuration).
## Usage
```go
package main
import (
"fmt"
str2duration "github.com/xhit/go-str2duration/v2"
"os"
"time"
)
func main() {
for i, tt := range []struct {
dur string
expected time.Duration
}{
//This times are returned with time.Duration string
{"1h", time.Duration(time.Hour)},
{"1m", time.Duration(time.Minute)},
{"1s", time.Duration(time.Second)},
{"1ms", time.Duration(time.Millisecond)},
{"1µs", time.Duration(time.Microsecond)},
{"1us", time.Duration(time.Microsecond)},
{"1ns", time.Duration(time.Nanosecond)},
{"4.000000001s", time.Duration(4*time.Second + time.Nanosecond)},
{"1h0m4.000000001s", time.Duration(time.Hour + 4*time.Second + time.Nanosecond)},
{"1h1m0.01s", time.Duration(61*time.Minute + 10*time.Millisecond)},
{"1h1m0.123456789s", time.Duration(61*time.Minute + 123456789*time.Nanosecond)},
{"1.00002ms", time.Duration(time.Millisecond + 20*time.Nanosecond)},
{"1.00000002s", time.Duration(time.Second + 20*time.Nanosecond)},
{"693ns", time.Duration(693 * time.Nanosecond)},
//This times aren't returned with time.Duration string, but are easily readable and can be parsed too!
{"1ms1ns", time.Duration(time.Millisecond + 1*time.Nanosecond)},
{"1s20ns", time.Duration(time.Second + 20*time.Nanosecond)},
{"60h8ms", time.Duration(60*time.Hour + 8*time.Millisecond)},
{"96h63s", time.Duration(96*time.Hour + 63*time.Second)},
//And works with days and weeks!
{"2d3s96ns", time.Duration(48*time.Hour + 3*time.Second + 96*time.Nanosecond)},
{"1w2d3s96ns", time.Duration(168*time.Hour + 48*time.Hour + 3*time.Second + 96*time.Nanosecond)},
{"10s1us693ns", time.Duration(10*time.Second + time.Microsecond + 693*time.Nanosecond)},
} {
durationFromString, err := str2duration.ParseDuration(tt.dur)
if err != nil {
panic(err)
//Check if expected time is the time returned by the parser
} else if tt.expected != durationFromString {
fmt.Println(fmt.Sprintf("index %d -> in: %s returned: %s\tnot equal to %s", i, tt.dur, durationFromString.String(), tt.expected.String()))
}else{
fmt.Println(fmt.Sprintf("index %d -> in: %s parsed succesfully", i, tt.dur))
}
}
}
```
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. golang-github-xhit-go-str2duration-2.1.0/go.mod 0000664 0000000 0000000 00000000063 14447354234 0021373 0 ustar 00root root 0000000 0000000 module github.com/xhit/go-str2duration/v2
go 1.13
golang-github-xhit-go-str2duration-2.1.0/str2duration.go 0000664 0000000 0000000 00000014553 14447354234 0023265 0 ustar 00root root 0000000 0000000 // 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 https://raw.githubusercontent.com/golang/go/master/LICENSE
package str2duration
import (
"errors"
"time"
)
var unitMap = map[string]int64{
"ns": int64(time.Nanosecond),
"us": int64(time.Microsecond),
"µs": int64(time.Microsecond), // U+00B5 = micro symbol
"μs": int64(time.Microsecond), // U+03BC = Greek letter mu
"ms": int64(time.Millisecond),
"s": int64(time.Second),
"m": int64(time.Minute),
"h": int64(time.Hour),
"d": int64(time.Hour) * 24,
"w": int64(time.Hour) * 168,
}
// ParseDuration parses a duration string.
// A duration string is a possibly signed sequence of
// decimal numbers, each with optional fraction and a unit suffix,
// such as "300ms", "-1.5h" or "2h45m".
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h", "d", "w".
func ParseDuration(s string) (time.Duration, error) {
// [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
orig := s
var d int64
neg := false
// Consume [-+]?
if s != "" {
c := s[0]
if c == '-' || c == '+' {
neg = c == '-'
s = s[1:]
}
}
// Special case: if all that is left is "0", this is zero.
if s == "0" {
return 0, nil
}
if s == "" {
return 0, errors.New("time: invalid duration " + quote(orig))
}
for s != "" {
var (
v, f int64 // integers before, after decimal point
scale float64 = 1 // value = v + f/scale
)
var err error
// The next character must be [0-9.]
if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
return 0, errors.New("time: invalid duration " + quote(orig))
}
// Consume [0-9]*
pl := len(s)
v, s, err = leadingInt(s)
if err != nil {
return 0, errors.New("time: invalid duration " + quote(orig))
}
pre := pl != len(s) // whether we consumed anything before a period
// Consume (\.[0-9]*)?
post := false
if s != "" && s[0] == '.' {
s = s[1:]
pl := len(s)
f, scale, s = leadingFraction(s)
post = pl != len(s)
}
if !pre && !post {
// no digits (e.g. ".s" or "-.s")
return 0, errors.New("time: invalid duration " + quote(orig))
}
// Consume unit.
i := 0
for ; i < len(s); i++ {
c := s[i]
if c == '.' || '0' <= c && c <= '9' {
break
}
}
if i == 0 {
return 0, errors.New("time: missing unit in duration " + quote(orig))
}
u := s[:i]
s = s[i:]
unit, ok := unitMap[u]
if !ok {
return 0, errors.New("time: unknown unit " + quote(u) + " in duration " + quote(orig))
}
if v > (1<<63-1)/unit {
// overflow
return 0, errors.New("time: invalid duration " + quote(orig))
}
v *= unit
if f > 0 {
// float64 is needed to be nanosecond accurate for fractions of hours.
// v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit)
v += int64(float64(f) * (float64(unit) / scale))
if v < 0 {
// overflow
return 0, errors.New("time: invalid duration " + quote(orig))
}
}
d += v
if d < 0 {
// overflow
return 0, errors.New("time: invalid duration " + quote(orig))
}
}
if neg {
d = -d
}
return time.Duration(d), nil
}
func quote(s string) string {
return "\"" + s + "\""
}
var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
// leadingInt consumes the leading [0-9]* from s.
func leadingInt(s string) (x int64, rem string, err error) {
i := 0
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if x > (1<<63-1)/10 {
// overflow
return 0, "", errLeadingInt
}
x = x*10 + int64(c) - '0'
if x < 0 {
// overflow
return 0, "", errLeadingInt
}
}
return x, s[i:], nil
}
// leadingFraction consumes the leading [0-9]* from s.
// It is used only for fractions, so does not return an error on overflow,
// it just stops accumulating precision.
func leadingFraction(s string) (x int64, scale float64, rem string) {
i := 0
scale = 1
overflow := false
for ; i < len(s); i++ {
c := s[i]
if c < '0' || c > '9' {
break
}
if overflow {
continue
}
if x > (1<<63-1)/10 {
// It's possible for overflow to give a positive number, so take care.
overflow = true
continue
}
y := x*10 + int64(c) - '0'
if y < 0 {
overflow = true
continue
}
x = y
scale *= 10
}
return x, scale, s[i:]
}
// String returns a string representing the duration in the form "1w4d2h3m5s".
// Units with 0 values aren't returned, for example: 1d1ms is 1 day 1 milliseconds
func String(d time.Duration) string {
if d == 0 {
return "0s"
}
// Largest time is 15250w1d23h47m16s854ms775us807ns
var buf [32]byte
w := len(buf)
var sign string
u := uint64(d)
neg := d < 0
if neg {
u = -u
sign = "-"
}
// u is nanoseconds (ns)
if u > 0 {
w--
if u%1000 > 0 {
buf[w] = 's'
w--
buf[w] = 'n'
w = fmtInt(buf[:w], u%1000)
} else {
w++
}
u /= 1000
// u is now integer microseconds (us)
if u > 0 {
w--
if u%1000 > 0 {
buf[w] = 's'
w--
buf[w] = 'u'
w = fmtInt(buf[:w], u%1000)
} else {
w++
}
u /= 1000
// u is now integer milliseconds (ms)
if u > 0 {
w--
if u%1000 > 0 {
buf[w] = 's'
w--
buf[w] = 'm'
w = fmtInt(buf[:w], u%1000)
} else {
w++
}
u /= 1000
// u is now integer seconds (s)
if u > 0 {
w--
if u%60 > 0 {
buf[w] = 's'
w = fmtInt(buf[:w], u%60)
} else {
w++
}
u /= 60
// u is now integer minutes (m)
if u > 0 {
w--
if u%60 > 0 {
buf[w] = 'm'
w = fmtInt(buf[:w], u%60)
} else {
w++
}
u /= 60
// u is now integer hours (h)
if u > 0 {
w--
if u%24 > 0 {
buf[w] = 'h'
w = fmtInt(buf[:w], u%24)
} else {
w++
}
u /= 24
// u is now integer days (d)
if u > 0 {
w--
if u%7 > 0 {
buf[w] = 'd'
w = fmtInt(buf[:w], u%7)
} else {
w++
}
u /= 7
// u is now integer weeks (w)
if u > 0 {
w--
buf[w] = 'w'
w = fmtInt(buf[:w], u)
}
}
}
}
}
}
}
}
return sign + string(buf[w:])
}
// 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
}
golang-github-xhit-go-str2duration-2.1.0/str2duration_test.go 0000664 0000000 0000000 00000015700 14447354234 0024317 0 ustar 00root root 0000000 0000000 package str2duration
import (
"testing"
"time"
)
func TestParseString(t *testing.T) {
for i, tt := range []struct {
dur string
expected time.Duration
}{
//This times are returned with time.Duration string
{"1h", time.Duration(time.Hour)},
{"1m", time.Duration(time.Minute)},
{"1s", time.Duration(time.Second)},
{"1ms", time.Duration(time.Millisecond)},
{"1µs", time.Duration(time.Microsecond)},
{"1us", time.Duration(time.Microsecond)},
{"1ns", time.Duration(time.Nanosecond)},
{"4.000000001s", time.Duration(4*time.Second + time.Nanosecond)},
{"1h0m4.000000001s", time.Duration(time.Hour + 4*time.Second + time.Nanosecond)},
{"1h1m0.01s", time.Duration(61*time.Minute + 10*time.Millisecond)},
{"1h1m0.123456789s", time.Duration(61*time.Minute + 123456789*time.Nanosecond)},
{"1.00002ms", time.Duration(time.Millisecond + 20*time.Nanosecond)},
{"1.00000002s", time.Duration(time.Second + 20*time.Nanosecond)},
{"693ns", time.Duration(693 * time.Nanosecond)},
{"10s1us693ns", time.Duration(10*time.Second + time.Microsecond + 693*time.Nanosecond)},
//This times aren't returned with time.Duration string, but are easily readable and can be parsed too!
{"1ms1ns", time.Duration(time.Millisecond + 1*time.Nanosecond)},
{"1s20ns", time.Duration(time.Second + 20*time.Nanosecond)},
{"60h8ms", time.Duration(60*time.Hour + 8*time.Millisecond)},
{"96h63s", time.Duration(96*time.Hour + 63*time.Second)},
//And works with days and weeks!
{"2d3s96ns", time.Duration(48*time.Hour + 3*time.Second + 96*time.Nanosecond)},
{"1w2d3s96ns", time.Duration(168*time.Hour + 48*time.Hour + 3*time.Second + 96*time.Nanosecond)},
{"1w2d3s3µs96ns", time.Duration(168*time.Hour + 48*time.Hour + 3*time.Second + 3*time.Microsecond + 96*time.Nanosecond)},
} {
durationFromString, err := ParseDuration(tt.dur)
if err != nil {
t.Logf("index %d -> in: %s returned: %s\tnot equal to %s", i, tt.dur, err.Error(), tt.expected.String())
} else if tt.expected != durationFromString {
t.Errorf("index %d -> in: %s returned: %s\tnot equal to %s", i, tt.dur, durationFromString.String(), tt.expected.String())
}
}
}
// TestParseDuration test if string returned by a duration is equal to string returned with the package
func TestParseDuration(t *testing.T) {
for i, duration := range []time.Duration{
time.Duration(time.Hour + time.Minute + time.Second + time.Millisecond + time.Microsecond + time.Nanosecond),
time.Duration(time.Minute + time.Second + time.Millisecond + time.Microsecond + time.Nanosecond),
time.Duration(time.Hour + time.Minute + time.Second + time.Millisecond + time.Microsecond + time.Nanosecond),
time.Duration(time.Minute + time.Second + time.Millisecond + time.Microsecond + time.Nanosecond),
time.Duration(time.Second + time.Millisecond + time.Microsecond + time.Nanosecond),
time.Duration(time.Millisecond + time.Microsecond + time.Nanosecond),
time.Duration(time.Microsecond + time.Nanosecond),
time.Duration(time.Nanosecond),
time.Duration(time.Hour + time.Minute + time.Second + time.Millisecond + time.Microsecond),
time.Duration(time.Minute + time.Second + time.Millisecond + time.Microsecond),
time.Duration(time.Second + time.Millisecond + time.Microsecond),
time.Duration(time.Millisecond + time.Microsecond),
time.Duration(time.Microsecond),
time.Duration(time.Hour + time.Minute + time.Second + time.Millisecond),
time.Duration(time.Minute + time.Second + time.Millisecond),
time.Duration(time.Second + time.Millisecond),
time.Duration(time.Millisecond),
time.Duration(time.Hour + time.Minute + time.Second),
time.Duration(time.Minute + time.Second),
time.Duration(time.Second),
time.Duration(time.Hour + time.Minute),
time.Duration(time.Minute),
time.Duration(time.Hour),
time.Duration(time.Millisecond + time.Nanosecond),
time.Duration(1001001 * time.Microsecond),
time.Duration(1001 * time.Nanosecond),
time.Duration(61 * time.Minute),
time.Duration(61 * time.Second),
time.Duration(time.Microsecond + 16*time.Nanosecond),
} {
durationFromString, _ := ParseDuration(duration.String())
if duration.String() != durationFromString.String() {
t.Errorf("index %d -> %s not equal to %s", i, duration.String(), durationFromString.String())
}
}
}
func TestString(t *testing.T) {
for i, tt := range []struct {
dur time.Duration
expected string
}{
//This times are returned with time.Duration string
{time.Duration(0), "0s"},
{time.Duration(time.Hour), "1h"},
{time.Duration(time.Minute), "1m"},
{time.Duration(time.Second), "1s"},
{time.Duration(time.Millisecond), "1ms"},
{time.Duration(time.Microsecond), "1us"},
{time.Duration(time.Nanosecond), "1ns"},
{time.Duration(4*time.Second + time.Nanosecond), "4s1ns"},
{time.Duration(time.Hour + 4*time.Second + time.Nanosecond), "1h4s1ns"},
{time.Duration(61*time.Minute + 10*time.Millisecond), "1h1m10ms"},
{time.Duration(61*time.Minute + 123456789*time.Nanosecond), "1h1m123ms456us789ns"},
{time.Duration(time.Millisecond + 20*time.Nanosecond), "1ms20ns"},
{time.Duration(time.Second + 20*time.Nanosecond), "1s20ns"},
{time.Duration(693 * time.Nanosecond), "693ns"},
{time.Duration(10*time.Second + time.Microsecond + 693*time.Nanosecond), "10s1us693ns"},
{time.Duration(time.Millisecond + 1*time.Nanosecond), "1ms1ns"},
{time.Duration(time.Second + 20*time.Nanosecond), "1s20ns"},
{time.Duration(60*time.Hour + 8*time.Millisecond), "2d12h8ms"},
{time.Duration(96*time.Hour + 63*time.Second), "4d1m3s"},
{time.Duration(48*time.Hour + 3*time.Second + 96*time.Nanosecond), "2d3s96ns"},
{time.Duration(168*time.Hour + 48*time.Hour + 3*time.Second + 96*time.Nanosecond), "1w2d3s96ns"},
{time.Duration(168*time.Hour + 48*time.Hour + 3*time.Second + 3*time.Microsecond + 96*time.Nanosecond), "1w2d3s3us96ns"},
// this is the maximum supported by golang std: 2540400h10m10.000000000s
{time.Duration(2540400*time.Hour + 10*time.Minute + 10*time.Second), "15121w3d10m10s"},
// this is max int64, the max duration supported to convert
{time.Duration(9_223_372_036_854_775_807 * time.Nanosecond), "15250w1d23h47m16s854ms775us807ns"},
// this is max int64 in negative, the max negative duration supported to convert
{time.Duration(-9_223_372_036_854_775_807 * time.Nanosecond), "-15250w1d23h47m16s854ms775us807ns"},
// negatives
{time.Duration(-time.Hour), "-1h"},
{time.Duration(-time.Minute), "-1m"},
{time.Duration(-time.Second), "-1s"},
{time.Duration(-time.Millisecond), "-1ms"},
{time.Duration(-time.Microsecond), "-1us"},
{time.Duration(-time.Nanosecond), "-1ns"},
{time.Duration(-4*time.Second - time.Nanosecond), "-4s1ns"},
} {
stringDuration := String(tt.dur)
if tt.expected != stringDuration {
t.Errorf("index %d -> in: %s returned: %s\tnot equal to %s", i, tt.dur, stringDuration, tt.expected)
}
durationParsed, _ := ParseDuration(stringDuration)
if durationParsed != tt.dur {
t.Errorf("error converting string to duration: index %d -> in: %s returned: %s", i, tt.dur, durationParsed)
}
}
}