From b5e08e3373db58093d1756d331dec3ed08cc851a Mon Sep 17 00:00:00 2001 From: Michael Yang Date: Wed, 11 Oct 2023 11:05:39 -0700 Subject: [PATCH] cleanup format time --- format/time.go | 97 ++++++--------------------------------------- format/time_test.go | 79 +++--------------------------------- 2 files changed, 18 insertions(+), 158 deletions(-) diff --git a/format/time.go b/format/time.go index a5a2ba53..6637c062 100644 --- a/format/time.go +++ b/format/time.go @@ -7,26 +7,14 @@ import ( "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 { +// humanDuration returns a human-readable approximation of a +// duration (eg. "About a minute", "4 hours ago", etc.). +func humanDuration(d time.Duration) string { seconds := int(d.Seconds()) switch { case seconds < 1: - if useCaps { - return "Less than a second" - } - return "less than a second" + return "Less than a second" case seconds == 1: return "1 second" case seconds < 60: @@ -36,10 +24,7 @@ func HumanDurationWithCase(d time.Duration, useCaps bool) string { minutes := int(d.Minutes()) switch { case minutes == 1: - if useCaps { - return "About a minute" - } - return "about a minute" + return "About a minute" case minutes < 60: return fmt.Sprintf("%d minutes", minutes) } @@ -47,10 +32,7 @@ func HumanDurationWithCase(d time.Duration, useCaps bool) string { hours := int(math.Round(d.Hours())) switch { case hours == 1: - if useCaps { - return "About an hour" - } - return "about an hour" + return "About an hour" case hours < 48: return fmt.Sprintf("%d hours", hours) case hours < 24*7*2: @@ -65,77 +47,22 @@ func HumanDurationWithCase(d time.Duration, useCaps bool) string { } func HumanTime(t time.Time, zeroValue string) string { - return humanTimeWithCase(t, zeroValue, true) + return humanTime(t, zeroValue) } func HumanTimeLower(t time.Time, zeroValue string) string { - return humanTimeWithCase(t, zeroValue, false) + return strings.ToLower(humanTime(t, zeroValue)) } -func humanTimeWithCase(t time.Time, zeroValue string, useCaps bool) string { +func humanTime(t time.Time, zeroValue string) string { if t.IsZero() { return zeroValue } delta := time.Since(t) if delta < 0 { - return HumanDurationWithCase(-delta, useCaps) + " from now" + return humanDuration(-delta) + " 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()) + + return humanDuration(delta) + " ago" } diff --git a/format/time_test.go b/format/time_test.go index 6f8bb4a9..b4e2db53 100644 --- a/format/time_test.go +++ b/format/time_test.go @@ -11,92 +11,25 @@ func assertEqual(t *testing.T, a interface{}, b interface{}) { } } -func TestHumanDuration(t *testing.T) { - day := 24 * time.Hour - week := 7 * day - month := 30 * day - year := 365 * day - - assertEqual(t, "Less than a second", HumanDuration(450*time.Millisecond)) - assertEqual(t, "Less than a second", HumanDurationWithCase(450*time.Millisecond, true)) - assertEqual(t, "less than a second", HumanDurationWithCase(450*time.Millisecond, false)) - assertEqual(t, "1 second", HumanDuration(1*time.Second)) - assertEqual(t, "45 seconds", HumanDuration(45*time.Second)) - assertEqual(t, "46 seconds", HumanDuration(46*time.Second)) - assertEqual(t, "59 seconds", HumanDuration(59*time.Second)) - assertEqual(t, "About a minute", HumanDuration(60*time.Second)) - assertEqual(t, "About a minute", HumanDurationWithCase(1*time.Minute, true)) - assertEqual(t, "about a minute", HumanDurationWithCase(1*time.Minute, false)) - assertEqual(t, "3 minutes", HumanDuration(3*time.Minute)) - assertEqual(t, "35 minutes", HumanDuration(35*time.Minute)) - assertEqual(t, "35 minutes", HumanDuration(35*time.Minute+40*time.Second)) - assertEqual(t, "45 minutes", HumanDuration(45*time.Minute)) - assertEqual(t, "45 minutes", HumanDuration(45*time.Minute+40*time.Second)) - assertEqual(t, "46 minutes", HumanDuration(46*time.Minute)) - assertEqual(t, "59 minutes", HumanDuration(59*time.Minute)) - assertEqual(t, "About an hour", HumanDuration(1*time.Hour)) - assertEqual(t, "About an hour", HumanDurationWithCase(1*time.Hour+29*time.Minute, true)) - assertEqual(t, "about an hour", HumanDurationWithCase(1*time.Hour+29*time.Minute, false)) - assertEqual(t, "2 hours", HumanDuration(1*time.Hour+31*time.Minute)) - assertEqual(t, "2 hours", HumanDuration(1*time.Hour+59*time.Minute)) - assertEqual(t, "3 hours", HumanDuration(3*time.Hour)) - assertEqual(t, "3 hours", HumanDuration(3*time.Hour+29*time.Minute)) - assertEqual(t, "4 hours", HumanDuration(3*time.Hour+31*time.Minute)) - assertEqual(t, "4 hours", HumanDuration(3*time.Hour+59*time.Minute)) - assertEqual(t, "4 hours", HumanDuration(3*time.Hour+60*time.Minute)) - assertEqual(t, "24 hours", HumanDuration(24*time.Hour)) - assertEqual(t, "36 hours", HumanDuration(1*day+12*time.Hour)) - assertEqual(t, "2 days", HumanDuration(2*day)) - assertEqual(t, "7 days", HumanDuration(7*day)) - assertEqual(t, "13 days", HumanDuration(13*day+5*time.Hour)) - assertEqual(t, "2 weeks", HumanDuration(2*week)) - assertEqual(t, "2 weeks", HumanDuration(2*week+4*day)) - assertEqual(t, "3 weeks", HumanDuration(3*week)) - assertEqual(t, "4 weeks", HumanDuration(4*week)) - assertEqual(t, "4 weeks", HumanDuration(4*week+3*day)) - assertEqual(t, "4 weeks", HumanDuration(1*month)) - assertEqual(t, "6 weeks", HumanDuration(1*month+2*week)) - assertEqual(t, "2 months", HumanDuration(2*month)) - assertEqual(t, "2 months", HumanDuration(2*month+2*week)) - assertEqual(t, "3 months", HumanDuration(3*month)) - assertEqual(t, "3 months", HumanDuration(3*month+1*week)) - assertEqual(t, "5 months", HumanDuration(5*month+2*week)) - assertEqual(t, "13 months", HumanDuration(13*month)) - assertEqual(t, "23 months", HumanDuration(23*month)) - assertEqual(t, "24 months", HumanDuration(24*month)) - assertEqual(t, "2 years", HumanDuration(24*month+2*week)) - assertEqual(t, "3 years", HumanDuration(3*year+2*month)) -} - func TestHumanTime(t *testing.T) { now := time.Now() t.Run("zero value", func(t *testing.T) { assertEqual(t, HumanTime(time.Time{}, "never"), "never") }) + t.Run("time in the future", func(t *testing.T) { v := now.Add(48 * time.Hour) assertEqual(t, HumanTime(v, ""), "2 days from now") }) + t.Run("time in the past", func(t *testing.T) { v := now.Add(-48 * time.Hour) assertEqual(t, HumanTime(v, ""), "2 days ago") }) -} -func TestExactDuration(t *testing.T) { - assertEqual(t, "1 millisecond", ExactDuration(1*time.Millisecond)) - assertEqual(t, "10 milliseconds", ExactDuration(10*time.Millisecond)) - assertEqual(t, "1 second", ExactDuration(1*time.Second)) - assertEqual(t, "10 seconds", ExactDuration(10*time.Second)) - assertEqual(t, "1 minute", ExactDuration(1*time.Minute)) - assertEqual(t, "10 minutes", ExactDuration(10*time.Minute)) - assertEqual(t, "1 hour", ExactDuration(1*time.Hour)) - assertEqual(t, "10 hours", ExactDuration(10*time.Hour)) - assertEqual(t, "1 hour 1 second", ExactDuration(1*time.Hour+1*time.Second)) - assertEqual(t, "1 hour 10 seconds", ExactDuration(1*time.Hour+10*time.Second)) - assertEqual(t, "1 hour 1 minute", ExactDuration(1*time.Hour+1*time.Minute)) - assertEqual(t, "1 hour 10 minutes", ExactDuration(1*time.Hour+10*time.Minute)) - assertEqual(t, "1 hour 1 minute 1 second", ExactDuration(1*time.Hour+1*time.Minute+1*time.Second)) - assertEqual(t, "10 hours 10 minutes 10 seconds", ExactDuration(10*time.Hour+10*time.Minute+10*time.Second)) + t.Run("soon", func(t *testing.T) { + v := now.Add(800*time.Millisecond) + assertEqual(t, HumanTime(v, ""), "Less than a second from now") + }) }