bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/opentsdb/duration.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package opentsdb 6 7 import ( 8 "errors" 9 "fmt" 10 "time" 11 ) 12 13 const ( 14 Millisecond Duration = Duration(time.Millisecond) 15 Second = 1000 * Millisecond 16 Minute = 60 * Second 17 Hour = 60 * Minute 18 Day = Hour * 24 19 Week = Day * 7 20 Month = Day * 30 21 Year = Day * 365 22 ) 23 24 // Duration extends time.Duration to support OpenTSDB time-format specifiers: 25 // http://opentsdb.net/docs/build/html/user_guide/query/dates.html#relative. 26 type Duration time.Duration 27 28 var unitMap = map[string]float64{ 29 "ms": float64(time.Millisecond), 30 "s": float64(time.Second), 31 "m": float64(time.Minute), 32 "h": float64(time.Hour), 33 "d": float64(time.Hour) * 24, 34 "w": float64(time.Hour) * 24 * 7, 35 "n": float64(time.Hour) * 24 * 30, 36 "y": float64(time.Hour) * 24 * 365, 37 } 38 39 // ParseDuration is equivalent to time.ParseDuration, but supports time units specified at http://opentsdb.net/docs/build/html/user_guide/query/dates.html. 40 func ParseDuration(s string) (Duration, error) { 41 // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ 42 orig := s 43 f := float64(0) 44 neg := false 45 46 // Consume [-+]? 47 if s != "" { 48 c := s[0] 49 if c == '-' || c == '+' { 50 neg = c == '-' 51 s = s[1:] 52 } 53 } 54 // Special case: if all that is left is "0", this is zero. 55 if s == "0" { 56 return 0, nil 57 } 58 if s == "" { 59 return 0, errors.New("time: invalid duration " + orig) 60 } 61 for s != "" { 62 g := float64(0) // this element of the sequence 63 64 var x int64 65 var err error 66 67 // The next character must be [0-9.] 68 if !(s[0] == '.' || ('0' <= s[0] && s[0] <= '9')) { 69 return 0, errors.New("time: invalid duration " + orig) 70 } 71 // Consume [0-9]* 72 pl := len(s) 73 x, s, err = leadingInt(s) 74 if err != nil { 75 return 0, errors.New("time: invalid duration " + orig) 76 } 77 g = float64(x) 78 pre := pl != len(s) // whether we consumed anything before a period 79 80 // Consume (\.[0-9]*)? 81 post := false 82 if s != "" && s[0] == '.' { 83 s = s[1:] 84 pl := len(s) 85 x, s, err = leadingInt(s) 86 if err != nil { 87 return 0, errors.New("time: invalid duration " + orig) 88 } 89 scale := 1.0 90 for n := pl - len(s); n > 0; n-- { 91 scale *= 10 92 } 93 g += float64(x) / scale 94 post = pl != len(s) 95 } 96 if !pre && !post { 97 // no digits (e.g. ".s" or "-.s") 98 return 0, errors.New("time: invalid duration " + orig) 99 } 100 101 // Consume unit. 102 i := 0 103 for ; i < len(s); i++ { 104 c := s[i] 105 if c == '.' || ('0' <= c && c <= '9') { 106 break 107 } 108 } 109 if i == 0 { 110 return 0, errors.New("time: missing unit in duration " + orig) 111 } 112 u := s[:i] 113 s = s[i:] 114 unit, ok := unitMap[u] 115 if !ok { 116 return 0, errors.New("time: unknown unit " + u + " in duration " + orig) 117 } 118 119 f += g * unit 120 } 121 122 if neg { 123 f = -f 124 } 125 return Duration(f), nil 126 } 127 128 var errLeadingInt = errors.New("time: bad [0-9]*") // never printed 129 130 // leadingInt consumes the leading [0-9]* from s. 131 func leadingInt(s string) (x int64, rem string, err error) { 132 i := 0 133 for ; i < len(s); i++ { 134 c := s[i] 135 if c < '0' || c > '9' { 136 break 137 } 138 if x >= (1<<63-10)/10 { 139 // overflow 140 return 0, "", errLeadingInt 141 } 142 x = x*10 + int64(c) - '0' 143 } 144 return x, s[i:], nil 145 } 146 147 func (d Duration) String() string { 148 return fmt.Sprintf("%dms", d/Millisecond) 149 } 150 151 func (d Duration) HumanString() string { 152 if d >= Year && d%Year == 0 { 153 return fmt.Sprintf("%dy", d/Year) 154 } 155 if d >= Week && d%Week == 0 { 156 return fmt.Sprintf("%dw", d/Week) 157 } 158 if d >= Day && d%Day == 0 { 159 return fmt.Sprintf("%dd", d/Day) 160 } 161 if d >= Hour && d%Hour == 0 { 162 return fmt.Sprintf("%dh", d/Hour) 163 } 164 if d >= Minute && d%Minute == 0 { 165 return fmt.Sprintf("%dm", d/Minute) 166 } 167 if d >= Second && d%Second == 0 { 168 return fmt.Sprintf("%ds", d/Second) 169 } 170 return fmt.Sprintf("%dms", d/Millisecond) 171 } 172 173 // Seconds returns the duration as a floating point number of seconds. 174 func (d Duration) Seconds() float64 { 175 return time.Duration(d).Seconds() 176 } 177 178 func (d *Duration) UnmarshalText(text []byte) error { 179 duration, err := ParseDuration(string(text)) 180 if err != nil { 181 return err 182 } 183 184 *d = duration 185 return nil 186 }