cleanup format time

This commit is contained in:
Michael Yang 2023-10-11 11:05:39 -07:00
parent 274d5a5fdf
commit b5e08e3373
2 changed files with 18 additions and 158 deletions

View file

@ -7,26 +7,14 @@ import (
"time" "time"
) )
// HumanDuration returns a human-readable approximation of a duration // humanDuration returns a human-readable approximation of a
// (eg. "About a minute", "4 hours ago", etc.). // duration (eg. "About a minute", "4 hours ago", etc.).
// Modified version of github.com/docker/go-units.HumanDuration func humanDuration(d time.Duration) string {
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()) seconds := int(d.Seconds())
switch { switch {
case seconds < 1: case seconds < 1:
if useCaps { return "Less than a second"
return "Less than a second"
}
return "less than a second"
case seconds == 1: case seconds == 1:
return "1 second" return "1 second"
case seconds < 60: case seconds < 60:
@ -36,10 +24,7 @@ func HumanDurationWithCase(d time.Duration, useCaps bool) string {
minutes := int(d.Minutes()) minutes := int(d.Minutes())
switch { switch {
case minutes == 1: case minutes == 1:
if useCaps { return "About a minute"
return "About a minute"
}
return "about a minute"
case minutes < 60: case minutes < 60:
return fmt.Sprintf("%d minutes", minutes) return fmt.Sprintf("%d minutes", minutes)
} }
@ -47,10 +32,7 @@ func HumanDurationWithCase(d time.Duration, useCaps bool) string {
hours := int(math.Round(d.Hours())) hours := int(math.Round(d.Hours()))
switch { switch {
case hours == 1: case hours == 1:
if useCaps { return "About an hour"
return "About an hour"
}
return "about an hour"
case hours < 48: case hours < 48:
return fmt.Sprintf("%d hours", hours) return fmt.Sprintf("%d hours", hours)
case hours < 24*7*2: 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 { 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 { 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() { if t.IsZero() {
return zeroValue return zeroValue
} }
delta := time.Since(t) delta := time.Since(t)
if delta < 0 { if delta < 0 {
return HumanDurationWithCase(-delta, useCaps) + " from now" return humanDuration(-delta) + " from now"
} }
return HumanDurationWithCase(delta, useCaps) + " ago"
} return humanDuration(delta) + " 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())
} }

View file

@ -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) { func TestHumanTime(t *testing.T) {
now := time.Now() now := time.Now()
t.Run("zero value", func(t *testing.T) { t.Run("zero value", func(t *testing.T) {
assertEqual(t, HumanTime(time.Time{}, "never"), "never") assertEqual(t, HumanTime(time.Time{}, "never"), "never")
}) })
t.Run("time in the future", func(t *testing.T) { t.Run("time in the future", func(t *testing.T) {
v := now.Add(48 * time.Hour) v := now.Add(48 * time.Hour)
assertEqual(t, HumanTime(v, ""), "2 days from now") assertEqual(t, HumanTime(v, ""), "2 days from now")
}) })
t.Run("time in the past", func(t *testing.T) { t.Run("time in the past", func(t *testing.T) {
v := now.Add(-48 * time.Hour) v := now.Add(-48 * time.Hour)
assertEqual(t, HumanTime(v, ""), "2 days ago") assertEqual(t, HumanTime(v, ""), "2 days ago")
}) })
}
func TestExactDuration(t *testing.T) { t.Run("soon", func(t *testing.T) {
assertEqual(t, "1 millisecond", ExactDuration(1*time.Millisecond)) v := now.Add(800*time.Millisecond)
assertEqual(t, "10 milliseconds", ExactDuration(10*time.Millisecond)) assertEqual(t, HumanTime(v, ""), "Less than a second from now")
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))
} }