package util import ( "fmt" "strconv" "time" ) const ( // SecsPerMin expresses the amount of seconds in a minute SecsPerMin = 60.0 // SecsPerHour expresses the amount of seconds in a minute SecsPerHour = 3600.0 // SecsPerDay expressed the amount of seconds in a day SecsPerDay = 86400.0 // MinsPerHour expresses the amount of minutes in an hour MinsPerHour = 60.0 // MinsPerDay expresses the amount of minutes in a day MinsPerDay = 1440.0 // HoursPerDay expresses the amount of hours in a day HoursPerDay = 24.0 // HoursPerMonth expresses the amount of hours in a month HoursPerMonth = 730.0 // DaysPerMonth expresses the amount of days in a month DaysPerMonth = 30.42 ) // DurationOffsetStrings converts a (duration, offset) pair to Prometheus- // compatible strings in terms of days, hours, minutes, or seconds. func DurationOffsetStrings(duration, offset time.Duration) (string, string) { durSecs := int64(duration.Seconds()) offSecs := int64(offset.Seconds()) durStr := "" if durSecs > 0 { if durSecs%SecsPerDay == 0 { // convert to days durStr = fmt.Sprintf("%dd", durSecs/SecsPerDay) } else if durSecs%SecsPerHour == 0 { // convert to hours durStr = fmt.Sprintf("%dh", durSecs/SecsPerHour) } else if durSecs%SecsPerMin == 0 { // convert to mins durStr = fmt.Sprintf("%dm", durSecs/SecsPerMin) } else if durSecs > 0 { // default to mins, as long as duration is positive durStr = fmt.Sprintf("%ds", durSecs) } } offStr := "" if offSecs > 0 { if offSecs%SecsPerDay == 0 { // convert to days offStr = fmt.Sprintf("%dd", offSecs/SecsPerDay) } else if offSecs%SecsPerHour == 0 { // convert to hours offStr = fmt.Sprintf("%dh", offSecs/SecsPerHour) } else if offSecs%SecsPerMin == 0 { // convert to mins offStr = fmt.Sprintf("%dm", offSecs/SecsPerMin) } else if offSecs > 0 { // default to mins, as long as offation is positive offStr = fmt.Sprintf("%ds", offSecs) } } return durStr, offStr } // ParseDuration converts a Prometheus-style duration string into a Duration func ParseDuration(duration string) (*time.Duration, error) { unitStr := duration[len(duration)-1:] var unit time.Duration switch unitStr { case "s": unit = time.Second case "m": unit = time.Minute case "h": unit = time.Hour case "d": unit = 24.0 * time.Hour default: return nil, fmt.Errorf("error parsing duration: %s did not match expected format [0-9+](s|m|d|h)", duration) } amountStr := duration[:len(duration)-1] amount, err := strconv.ParseInt(amountStr, 10, 64) if err != nil { return nil, fmt.Errorf("error parsing duration: %s did not match expected format [0-9+](s|m|d|h)", duration) } dur := time.Duration(amount) * unit return &dur, nil } // ParseTimeRange returns a start and end time, respectively, which are converted from // a duration and offset, defined as strings with Prometheus-style syntax. func ParseTimeRange(duration, offset string) (*time.Time, *time.Time, error) { // endTime defaults to the current time, unless an offset is explicity declared, // in which case it shifts endTime back by given duration endTime := time.Now() if offset != "" { o, err := ParseDuration(offset) if err != nil { return nil, nil, fmt.Errorf("error parsing offset (%s): %s", offset, err) } endTime = endTime.Add(-1 * *o) } // if duration is defined in terms of days, convert to hours // e.g. convert "2d" to "48h" durationNorm, err := normalizeTimeParam(duration) if err != nil { return nil, nil, fmt.Errorf("error parsing duration (%s): %s", duration, err) } // convert time duration into start and end times, formatted // as ISO datetime strings dur, err := time.ParseDuration(durationNorm) if err != nil { return nil, nil, fmt.Errorf("errorf parsing duration (%s): %s", durationNorm, err) } startTime := endTime.Add(-1 * dur) return &startTime, &endTime, nil } func normalizeTimeParam(param string) (string, error) { // convert days to hours if param[len(param)-1:] == "d" { count := param[:len(param)-1] val, err := strconv.ParseInt(count, 10, 64) if err != nil { return "", err } val = val * 24 param = fmt.Sprintf("%dh", val) } return param, nil }