package format import ( "fmt" "math" "strings" "time" ) // HumanDuration returns a human-readable approximation of a duration // (eg. "About a minute", "4 hours ago", etc.). // Modified version of github.com/docker/go-units.HumanDuration func HumanDuration(d time.Duration) string { return HumanDurationWithCase(d, true) } // HumanDurationWithCase returns a human-readable approximation of a // duration (eg. "About a minute", "4 hours ago", etc.). but allows // you to specify whether the first word should be capitalized // (eg. "About" vs. "about") func HumanDurationWithCase(d time.Duration, useCaps bool) string { seconds := int(d.Seconds()) switch { case seconds < 1: if useCaps { return "Less than a second" } return "less than a second" case seconds == 1: return "1 second" case seconds < 60: return fmt.Sprintf("%d seconds", seconds) } minutes := int(d.Minutes()) switch { case minutes == 1: if useCaps { return "About a minute" } return "about a minute" case minutes < 60: return fmt.Sprintf("%d minutes", minutes) } hours := int(math.Round(d.Hours())) switch { case hours == 1: if useCaps { return "About an hour" } return "about an hour" case hours < 48: return fmt.Sprintf("%d hours", hours) case hours < 24*7*2: return fmt.Sprintf("%d days", hours/24) case hours < 24*30*2: return fmt.Sprintf("%d weeks", hours/24/7) case hours < 24*365*2: return fmt.Sprintf("%d months", hours/24/30) } return fmt.Sprintf("%d years", int(d.Hours())/24/365) } func HumanTime(t time.Time, zeroValue string) string { return humanTimeWithCase(t, zeroValue, true) } func HumanTimeLower(t time.Time, zeroValue string) string { return humanTimeWithCase(t, zeroValue, false) } func humanTimeWithCase(t time.Time, zeroValue string, useCaps bool) string { if t.IsZero() { return zeroValue } delta := time.Since(t) if delta < 0 { return HumanDurationWithCase(-delta, useCaps) + " from now" } return HumanDurationWithCase(delta, useCaps) + " ago" } // ExcatDuration returns a human readable hours/minutes/seconds or milliseconds format of a duration // the most precise level of duration is milliseconds func ExactDuration(d time.Duration) string { if d.Seconds() < 1 { if d.Milliseconds() == 1 { return fmt.Sprintf("%d millisecond", d.Milliseconds()) } return fmt.Sprintf("%d milliseconds", d.Milliseconds()) } var readableDur strings.Builder dur := d.String() // split the default duration string format of 0h0m0s into something nicer to read h := strings.Split(dur, "h") if len(h) > 1 { hours := h[0] if hours == "1" { readableDur.WriteString(fmt.Sprintf("%s hour ", hours)) } else { readableDur.WriteString(fmt.Sprintf("%s hours ", hours)) } dur = h[1] } m := strings.Split(dur, "m") if len(m) > 1 { mins := m[0] switch mins { case "0": // skip case "1": readableDur.WriteString(fmt.Sprintf("%s minute ", mins)) default: readableDur.WriteString(fmt.Sprintf("%s minutes ", mins)) } dur = m[1] } s := strings.Split(dur, "s") if len(s) > 0 { sec := s[0] switch sec { case "0": // skip case "1": readableDur.WriteString(fmt.Sprintf("%s second ", sec)) default: readableDur.WriteString(fmt.Sprintf("%s seconds ", sec)) } } return strings.TrimSpace(readableDur.String()) }