package runexpr import ( "fmt" "log" "math" "strconv" "time" ) const maxDuration time.Duration = 1<<63 - 1 // humanDuration represents a ES duration with day support type humanDuration struct { days, hours, minutes, seconds int } func parseDuration(duration string) time.Duration { var seconds int64 for _, match := range matchDuration.FindAllStringSubmatch(duration, -1) { if len(match) == 3 { durationvalue, err := strconv.ParseInt(match[1], 10, 64) if err != nil { log.Fatal(err) } switch match[2][0] { case 'y': seconds += durationvalue * 31536000 case 'd': seconds += durationvalue * 86400 case 'h': seconds += durationvalue * 3600 case 'm': seconds += durationvalue * 60 case 's': seconds += durationvalue } } } if seconds >= int64(maxDuration.Seconds()) { log.Fatal("duration overflow, choose a smaller duration") } dur := time.Duration(seconds) * time.Second return dur } // extractDuration parses a ES duration and returns an esDuration func extractDuration(duration string) humanDuration { esd := humanDuration{} for _, match := range matchDuration.FindAllStringSubmatch(duration, -1) { if len(match) == 3 { durationvalue, _ := strconv.Atoi(match[1]) switch match[2][0] { case 'd': esd.days = durationvalue case 'h': esd.hours = durationvalue case 'm': esd.minutes = durationvalue case 's': esd.seconds = durationvalue } } } return esd } // formatDays returns days string, unless days is larger than 365, // then it returns years and days, e.g. 3y/34d func formatDays(days float64) string { if days > 365 { years := days / 365 days := math.Remainder(days, 365) return fmt.Sprintf("%dy/%dd", int64(years), int64(days)) } return fmt.Sprintf("%dd", int64(days)) } // formatDuration converts a time.Duration to a prettier human // readable form, like 156h6m15.095s-6d/12h/6m => 6d/12h/6m func formatDuration(val time.Duration) string { esd := extractDuration(val.String()) daysF := float64(esd.hours) / 24.0 days := math.Floor(daysF) hoursLeft := (daysF - days) * 24 switch { case val.Minutes() < 1: return fmt.Sprintf("%ds", int64(esd.seconds)) case val.Hours() < 1: return fmt.Sprintf("%dm", int64(esd.minutes)) case val.Hours() < 24: if esd.minutes == 0 { return fmt.Sprintf("%dh", esd.hours) } return fmt.Sprintf("%dh/%dm", esd.hours, int64(esd.minutes)) default: if esd.minutes == 0 && hoursLeft == 0 { return formatDays(days) } return fmt.Sprintf("%s/%dh/%dm", formatDays(days), int64(hoursLeft), esd.minutes, ) } } func toYears(d time.Duration) float64 { return d.Hours() / 24 / 365 } func toDays(d time.Duration) float64 { return d.Hours() / 24 } func toHours(d time.Duration) float64 { return d.Hours() } func toMinutes(d time.Duration) float64 { return d.Minutes() } func toSeconds(d time.Duration) float64 { return d.Seconds() }