pax_global_header00006660000000000000000000000064144473542340014524gustar00rootroot0000000000000052 comment=4e55dcca3035f895aa7ac90c6cf3c8c299136411 golang-github-xhit-go-str2duration-2.1.0/000077500000000000000000000000001444735423400202665ustar00rootroot00000000000000golang-github-xhit-go-str2duration-2.1.0/.github/000077500000000000000000000000001444735423400216265ustar00rootroot00000000000000golang-github-xhit-go-str2duration-2.1.0/.github/FUNDING.yml000066400000000000000000000012311444735423400234400ustar00rootroot00000000000000# 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/LICENSE000066400000000000000000000027061444735423400213000ustar00rootroot00000000000000Copyright (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.md000066400000000000000000000075361444735423400215600ustar00rootroot00000000000000# 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!. Go Report Card go.dev ## 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.mod000066400000000000000000000000631444735423400213730ustar00rootroot00000000000000module github.com/xhit/go-str2duration/v2 go 1.13 golang-github-xhit-go-str2duration-2.1.0/str2duration.go000066400000000000000000000145531444735423400232650ustar00rootroot00000000000000// 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.go000066400000000000000000000157001444735423400243170ustar00rootroot00000000000000package 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) } } }