1// Copyright 2015 go-swagger maintainers 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package strfmt 16 17import ( 18 "database/sql/driver" 19 "encoding/json" 20 "errors" 21 "fmt" 22 "regexp" 23 "strconv" 24 "strings" 25 "time" 26 27 "go.mongodb.org/mongo-driver/bson" 28) 29 30func init() { 31 d := Duration(0) 32 // register this format in the default registry 33 Default.Add("duration", &d, IsDuration) 34} 35 36var ( 37 timeUnits = [][]string{ 38 {"ns", "nano"}, 39 {"us", "µs", "micro"}, 40 {"ms", "milli"}, 41 {"s", "sec"}, 42 {"m", "min"}, 43 {"h", "hr", "hour"}, 44 {"d", "day"}, 45 {"w", "wk", "week"}, 46 } 47 48 timeMultiplier = map[string]time.Duration{ 49 "ns": time.Nanosecond, 50 "us": time.Microsecond, 51 "ms": time.Millisecond, 52 "s": time.Second, 53 "m": time.Minute, 54 "h": time.Hour, 55 "d": 24 * time.Hour, 56 "w": 7 * 24 * time.Hour, 57 } 58 59 durationMatcher = regexp.MustCompile(`((\d+)\s*([A-Za-zµ]+))`) 60) 61 62// IsDuration returns true if the provided string is a valid duration 63func IsDuration(str string) bool { 64 _, err := ParseDuration(str) 65 return err == nil 66} 67 68// Duration represents a duration 69// 70// Duration stores a period of time as a nanosecond count, with the largest 71// repesentable duration being approximately 290 years. 72// 73// swagger:strfmt duration 74type Duration time.Duration 75 76// MarshalText turns this instance into text 77func (d Duration) MarshalText() ([]byte, error) { 78 return []byte(time.Duration(d).String()), nil 79} 80 81// UnmarshalText hydrates this instance from text 82func (d *Duration) UnmarshalText(data []byte) error { // validation is performed later on 83 dd, err := ParseDuration(string(data)) 84 if err != nil { 85 return err 86 } 87 *d = Duration(dd) 88 return nil 89} 90 91// ParseDuration parses a duration from a string, compatible with scala duration syntax 92func ParseDuration(cand string) (time.Duration, error) { 93 if dur, err := time.ParseDuration(cand); err == nil { 94 return dur, nil 95 } 96 97 var dur time.Duration 98 ok := false 99 for _, match := range durationMatcher.FindAllStringSubmatch(cand, -1) { 100 101 factor, err := strconv.Atoi(match[2]) // converts string to int 102 if err != nil { 103 return 0, err 104 } 105 unit := strings.ToLower(strings.TrimSpace(match[3])) 106 107 for _, variants := range timeUnits { 108 last := len(variants) - 1 109 multiplier := timeMultiplier[variants[0]] 110 111 for i, variant := range variants { 112 if (last == i && strings.HasPrefix(unit, variant)) || strings.EqualFold(variant, unit) { 113 ok = true 114 dur += (time.Duration(factor) * multiplier) 115 } 116 } 117 } 118 } 119 120 if ok { 121 return dur, nil 122 } 123 return 0, fmt.Errorf("unable to parse %s as duration", cand) 124} 125 126// Scan reads a Duration value from database driver type. 127func (d *Duration) Scan(raw interface{}) error { 128 switch v := raw.(type) { 129 // TODO: case []byte: // ? 130 case int64: 131 *d = Duration(v) 132 case float64: 133 *d = Duration(int64(v)) 134 case nil: 135 *d = Duration(0) 136 default: 137 return fmt.Errorf("cannot sql.Scan() strfmt.Duration from: %#v", v) 138 } 139 140 return nil 141} 142 143// Value converts Duration to a primitive value ready to be written to a database. 144func (d Duration) Value() (driver.Value, error) { 145 return driver.Value(int64(d)), nil 146} 147 148// String converts this duration to a string 149func (d Duration) String() string { 150 return time.Duration(d).String() 151} 152 153// MarshalJSON returns the Duration as JSON 154func (d Duration) MarshalJSON() ([]byte, error) { 155 return json.Marshal(time.Duration(d).String()) 156} 157 158// UnmarshalJSON sets the Duration from JSON 159func (d *Duration) UnmarshalJSON(data []byte) error { 160 if string(data) == jsonNull { 161 return nil 162 } 163 164 var dstr string 165 if err := json.Unmarshal(data, &dstr); err != nil { 166 return err 167 } 168 tt, err := ParseDuration(dstr) 169 if err != nil { 170 return err 171 } 172 *d = Duration(tt) 173 return nil 174} 175 176func (d Duration) MarshalBSON() ([]byte, error) { 177 return bson.Marshal(bson.M{"data": d.String()}) 178} 179 180func (d *Duration) UnmarshalBSON(data []byte) error { 181 var m bson.M 182 if err := bson.Unmarshal(data, &m); err != nil { 183 return err 184 } 185 186 if data, ok := m["data"].(string); ok { 187 rd, err := ParseDuration(data) 188 if err != nil { 189 return err 190 } 191 *d = Duration(rd) 192 return nil 193 } 194 195 return errors.New("couldn't unmarshal bson bytes value as Date") 196} 197 198// DeepCopyInto copies the receiver and writes its value into out. 199func (d *Duration) DeepCopyInto(out *Duration) { 200 *out = *d 201} 202 203// DeepCopy copies the receiver into a new Duration. 204func (d *Duration) DeepCopy() *Duration { 205 if d == nil { 206 return nil 207 } 208 out := new(Duration) 209 d.DeepCopyInto(out) 210 return out 211} 212