package timeext import ( "errors" "fmt" "strconv" "strings" "time" ) var durationShortStringMap = map[string]time.Duration{ "ns": time.Nanosecond, "nanosecond": time.Nanosecond, "nanoseconds": time.Nanosecond, "us": time.Microsecond, "microsecond": time.Microsecond, "microseconds": time.Microsecond, "ms": time.Millisecond, "millisecond": time.Millisecond, "milliseconds": time.Millisecond, "s": time.Second, "sec": time.Second, "second": time.Second, "seconds": time.Second, "m": time.Minute, "min": time.Minute, "minute": time.Minute, "minutes": time.Minute, "h": time.Hour, "hour": time.Hour, "hours": time.Hour, "d": 24 * time.Hour, "day": 24 * time.Hour, "days": 24 * time.Hour, "w": 7 * 24 * time.Hour, "wk": 7 * 24 * time.Hour, "week": 7 * 24 * time.Hour, "weeks": 7 * 24 * time.Hour, } // ParseDurationShortString parses a duration in string format to a time.Duration // Examples for allowed formats: // - '10m' // - '10min' // - '1minute' // - '10minutes' // - '10.5minutes' // - '50s' // - '50sec' // - '1second' // - '50seconds' // - '100ms' // - '100millisseconds' // - '1h' // - '1hour' // - '2hours' // - '1d' // - '1day' // - '10days' // - '1d10m' // - '1d10m200sec' // - '1d:10m' // - '1d 10m' // - '1d,10m' func ParseDurationShortString(s string) (time.Duration, error) { s = strings.ToLower(s) segments := make([]string, 0) collector := "" prevWasNum := true for _, chr := range s { if chr >= '0' && chr <= '9' || chr == '.' { if prevWasNum { collector += string(chr) } else { segments = append(segments, collector) prevWasNum = true collector = string(chr) } } else if chr == ' ' || chr == ':' || chr == ',' { continue } else if chr >= 'a' && chr <= 'z' { prevWasNum = false collector += string(chr) } else { return 0, errors.New("unexpected character: " + string(chr)) } } if !prevWasNum { segments = append(segments, collector) } result := time.Duration(0) for _, seg := range segments { segDur, err := parseDurationShortStringSegment(seg) if err != nil { return 0, err } result += segDur } return result, nil } func parseDurationShortStringSegment(segment string) (time.Duration, error) { num := "" unit := "" part0 := true for _, chr := range segment { if part0 { if chr >= 'a' && chr <= 'z' { part0 = false unit += string(chr) } else if chr >= '0' && chr <= '9' || chr == '.' { num += string(chr) } else { return 0, errors.New(fmt.Sprintf("Unexpected character '%s' in segment [%s]", string(chr), segment)) } } else { if chr >= 'a' && chr <= 'z' { unit += string(chr) } else if chr >= '0' && chr <= '9' || chr == '.' { return 0, errors.New(fmt.Sprintf("Unexpected number '%s' in segment [%s]", string(chr), segment)) } else { return 0, errors.New(fmt.Sprintf("Unexpected character '%s' in segment [%s]", string(chr), segment)) } } } fpnum, err := strconv.ParseFloat(num, 64) if err != nil { return 0, errors.New(fmt.Sprintf("Failed to parse floating-point number '%s' in segment [%s]", num, segment)) } if mult, ok := durationShortStringMap[unit]; ok { return time.Duration(int64(fpnum * float64(mult))), nil } else { return 0, errors.New(fmt.Sprintf("Unknown unit '%s' in segment [%s]", unit, segment)) } }