Skip to content

Commit

Permalink
feat: Better duration to human string
Browse files Browse the repository at this point in the history
  • Loading branch information
F1bonacc1 committed Dec 20, 2024
1 parent 05fa613 commit a7e6c06
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 91 deletions.
63 changes: 47 additions & 16 deletions src/app/pc_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,52 @@ func isStringDefined(str string) bool {
return len(strings.TrimSpace(str)) > 0
}

func durationToString(dur time.Duration) string {
if dur.Minutes() < 3 {
//seconds
return dur.Round(time.Second).String()
} else if dur.Minutes() < 60 {
//minutes
return fmt.Sprintf("%.0fm", dur.Minutes())
} else if dur.Hours() < 24 {
//hours and minutes
return fmt.Sprintf("%dh%dm", int(dur.Hours()), int(dur.Minutes())%60)
} else if dur.Hours() < 48 {
//days and hours
return fmt.Sprintf("%dd%dh", int(dur.Hours())/24, int(dur.Hours())%24)
} else {
//days
return fmt.Sprintf("%dd", int(dur.Hours())/24)
// HumanDuration returns a succinct representation of the provided duration
// with limited precision for consumption by humans. It provides ~2-3 significant
// figures of duration.
func HumanDuration(d time.Duration) string {
// Allow deviation no more than 2 seconds(excluded) to tolerate machine time
// inconsistence, it can be considered as almost now.
if seconds := int(d.Seconds()); seconds < -1 {
return "<invalid>"
} else if seconds < 0 {
return "0s"
} else if seconds < 60*2 {
return fmt.Sprintf("%ds", seconds)
}
minutes := int(d / time.Minute)
if minutes < 10 {
s := int(d/time.Second) % 60
if s == 0 {
return fmt.Sprintf("%dm", minutes)
}
return fmt.Sprintf("%dm%ds", minutes, s)
} else if minutes < 60*3 {
return fmt.Sprintf("%dm", minutes)
}
hours := int(d / time.Hour)
if hours < 8 {
m := int(d/time.Minute) % 60
if m == 0 {
return fmt.Sprintf("%dh", hours)
}
return fmt.Sprintf("%dh%dm", hours, m)
} else if hours < 48 {
return fmt.Sprintf("%dh", hours)
} else if hours < 24*8 {
h := hours % 24
if h == 0 {
return fmt.Sprintf("%dd", hours/24)
}
return fmt.Sprintf("%dd%dh", hours/24, h)
} else if hours < 24*365*2 {
return fmt.Sprintf("%dd", hours/24)
} else if hours < 24*365*8 {
dy := int(hours/24) % 365
if dy == 0 {
return fmt.Sprintf("%dy", hours/24/365)
}
return fmt.Sprintf("%dy%dd", hours/24/365, dy)
}
return fmt.Sprintf("%dy", int(hours/24/365))
}
83 changes: 83 additions & 0 deletions src/app/pc_string_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package app

import (
"testing"
"time"
)

func TestHumanDuration(t *testing.T) {
tests := []struct {
d time.Duration
want string
}{
{d: time.Second, want: "1s"},
{d: 70 * time.Second, want: "70s"},
{d: 190 * time.Second, want: "3m10s"},
{d: 70 * time.Minute, want: "70m"},
{d: 47 * time.Hour, want: "47h"},
{d: 49 * time.Hour, want: "2d1h"},
{d: (8*24 + 2) * time.Hour, want: "8d"},
{d: (367 * 24) * time.Hour, want: "367d"},
{d: (365*2*24 + 25) * time.Hour, want: "2y1d"},
{d: (365*8*24 + 2) * time.Hour, want: "8y"},
}
for _, tt := range tests {
t.Run(tt.d.String(), func(t *testing.T) {
if got := HumanDuration(tt.d); got != tt.want {
t.Errorf("HumanDuration() = %v, want %v", got, tt.want)
}
})
}
}

func TestHumanDurationBoundaries(t *testing.T) {
tests := []struct {
d time.Duration
want string
}{
{d: -2 * time.Second, want: "<invalid>"},
{d: -2*time.Second + 1, want: "0s"},
{d: 0, want: "0s"},
{d: time.Second - time.Millisecond, want: "0s"},
{d: 2*time.Minute - time.Millisecond, want: "119s"},
{d: 2 * time.Minute, want: "2m"},
{d: 2*time.Minute + time.Second, want: "2m1s"},
{d: 10*time.Minute - time.Millisecond, want: "9m59s"},
{d: 10 * time.Minute, want: "10m"},
{d: 10*time.Minute + time.Second, want: "10m"},
{d: 3*time.Hour - time.Millisecond, want: "179m"},
{d: 3 * time.Hour, want: "3h"},
{d: 3*time.Hour + time.Minute, want: "3h1m"},
{d: 8*time.Hour - time.Millisecond, want: "7h59m"},
{d: 8 * time.Hour, want: "8h"},
{d: 8*time.Hour + 59*time.Minute, want: "8h"},
{d: 2*24*time.Hour - time.Millisecond, want: "47h"},
{d: 2 * 24 * time.Hour, want: "2d"},
{d: 2*24*time.Hour + time.Hour, want: "2d1h"},
{d: 8*24*time.Hour - time.Millisecond, want: "7d23h"},
{d: 8 * 24 * time.Hour, want: "8d"},
{d: 8*24*time.Hour + 23*time.Hour, want: "8d"},
{d: 2*365*24*time.Hour - time.Millisecond, want: "729d"},
{d: 2 * 365 * 24 * time.Hour, want: "2y"},
{d: 2*365*24*time.Hour + 23*time.Hour, want: "2y"},
{d: 2*365*24*time.Hour + 23*time.Hour + 59*time.Minute, want: "2y"},
{d: 2*365*24*time.Hour + 24*time.Hour - time.Millisecond, want: "2y"},
{d: 2*365*24*time.Hour + 24*time.Hour, want: "2y1d"},
{d: 3 * 365 * 24 * time.Hour, want: "3y"},
{d: 4 * 365 * 24 * time.Hour, want: "4y"},
{d: 5 * 365 * 24 * time.Hour, want: "5y"},
{d: 6 * 365 * 24 * time.Hour, want: "6y"},
{d: 7 * 365 * 24 * time.Hour, want: "7y"},
{d: 8*365*24*time.Hour - time.Millisecond, want: "7y364d"},
{d: 8 * 365 * 24 * time.Hour, want: "8y"},
{d: 8*365*24*time.Hour + 364*24*time.Hour, want: "8y"},
{d: 9 * 365 * 24 * time.Hour, want: "9y"},
}
for _, tt := range tests {
t.Run(tt.d.String(), func(t *testing.T) {
if got := HumanDuration(tt.d); got != tt.want {
t.Errorf("HumanDuration() = %v, want %v", got, tt.want)
}
})
}
}
2 changes: 1 addition & 1 deletion src/app/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ func (p *Process) updateProcState() {
defer p.stateMtx.Unlock()
if isRunning {
dur := time.Since(p.getStartTime())
p.procState.SystemTime = durationToString(dur)
p.procState.SystemTime = HumanDuration(dur)
p.procState.Age = dur
p.procState.Name = p.getName()
p.procState.Mem, p.procState.CPU = p.getResourceUsage()
Expand Down
74 changes: 0 additions & 74 deletions src/app/process_test.go

This file was deleted.

0 comments on commit a7e6c06

Please sign in to comment.