fs: Higher units for ETA - fixes #3221
This commit is contained in:
parent
7b2b396d37
commit
70c6b01f54
4 changed files with 112 additions and 1 deletions
|
@ -172,7 +172,7 @@ func etaString(done, total int64, rate float64) string {
|
||||||
if !ok {
|
if !ok {
|
||||||
return "-"
|
return "-"
|
||||||
}
|
}
|
||||||
return d.String()
|
return fs.Duration(d).ReadableString()
|
||||||
}
|
}
|
||||||
|
|
||||||
// percent returns a/b as a percentage rounded to the nearest integer
|
// percent returns a/b as a percentage rounded to the nearest integer
|
||||||
|
|
|
@ -19,9 +19,20 @@ func TestETA(t *testing.T) {
|
||||||
wantOK bool
|
wantOK bool
|
||||||
wantString string
|
wantString string
|
||||||
}{
|
}{
|
||||||
|
// Custom String Cases
|
||||||
|
{size: 0, total: 365 * 86400, rate: 1.0, wantETA: 365 * 86400 * time.Second, wantOK: true, wantString: "1y"},
|
||||||
|
{size: 0, total: 7 * 86400, rate: 1.0, wantETA: 7 * 86400 * time.Second, wantOK: true, wantString: "1w"},
|
||||||
|
{size: 0, total: 1 * 86400, rate: 1.0, wantETA: 1 * 86400 * time.Second, wantOK: true, wantString: "1d"},
|
||||||
|
{size: 0, total: 1110 * 86400, rate: 1.0, wantETA: 1110 * 86400 * time.Second, wantOK: true, wantString: "3y2w1d"},
|
||||||
|
{size: 0, total: 15 * 86400, rate: 1.0, wantETA: 15 * 86400 * time.Second, wantOK: true, wantString: "2w1d"},
|
||||||
|
// Composite Custom String Cases
|
||||||
|
{size: 0, total: 1.5 * 86400, rate: 1.0, wantETA: 1.5 * 86400 * time.Second, wantOK: true, wantString: "1d12h"},
|
||||||
|
{size: 0, total: 95000, rate: 1.0, wantETA: 95000 * time.Second, wantOK: true, wantString: "1d2h23m20s"},
|
||||||
|
// Standard Duration String Cases
|
||||||
{size: 0, total: 100, rate: 1.0, wantETA: 100 * time.Second, wantOK: true, wantString: "1m40s"},
|
{size: 0, total: 100, rate: 1.0, wantETA: 100 * time.Second, wantOK: true, wantString: "1m40s"},
|
||||||
{size: 50, total: 100, rate: 1.0, wantETA: 50 * time.Second, wantOK: true, wantString: "50s"},
|
{size: 50, total: 100, rate: 1.0, wantETA: 50 * time.Second, wantOK: true, wantString: "50s"},
|
||||||
{size: 100, total: 100, rate: 1.0, wantETA: 0 * time.Second, wantOK: true, wantString: "0s"},
|
{size: 100, total: 100, rate: 1.0, wantETA: 0 * time.Second, wantOK: true, wantString: "0s"},
|
||||||
|
// No String Cases
|
||||||
{size: -1, total: 100, rate: 1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
{size: -1, total: 100, rate: 1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
||||||
{size: 200, total: 100, rate: 1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
{size: 200, total: 100, rate: 1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
||||||
{size: 10, total: -1, rate: 1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
{size: 10, total: -1, rate: 1.0, wantETA: 0, wantOK: false, wantString: "-"},
|
||||||
|
|
|
@ -78,6 +78,68 @@ func ParseDuration(age string) (time.Duration, error) {
|
||||||
return time.Duration(period), nil
|
return time.Duration(period), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadableString parses d into a human readable duration.
|
||||||
|
// Based on https://github.com/hako/durafmt
|
||||||
|
func (d Duration) ReadableString() string {
|
||||||
|
switch d {
|
||||||
|
case DurationOff:
|
||||||
|
return "off"
|
||||||
|
case 0:
|
||||||
|
return "0s"
|
||||||
|
}
|
||||||
|
|
||||||
|
readableString := ""
|
||||||
|
|
||||||
|
// Check for minus durations.
|
||||||
|
if d < 0 {
|
||||||
|
readableString += "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
duration := time.Duration(math.Abs(float64(d)))
|
||||||
|
|
||||||
|
// Convert duration.
|
||||||
|
seconds := int64(duration.Seconds()) % 60
|
||||||
|
minutes := int64(duration.Minutes()) % 60
|
||||||
|
hours := int64(duration.Hours()) % 24
|
||||||
|
days := int64(duration/(24*time.Hour)) % 365 % 7
|
||||||
|
|
||||||
|
// Edge case between 364 and 365 days.
|
||||||
|
// We need to calculate weeks from what is left from years
|
||||||
|
leftYearDays := int64(duration/(24*time.Hour)) % 365
|
||||||
|
weeks := leftYearDays / 7
|
||||||
|
if leftYearDays >= 364 && leftYearDays < 365 {
|
||||||
|
weeks = 52
|
||||||
|
}
|
||||||
|
|
||||||
|
years := int64(duration/(24*time.Hour)) / 365
|
||||||
|
milliseconds := int64(duration/time.Millisecond) -
|
||||||
|
(seconds * 1000) - (minutes * 60000) - (hours * 3600000) -
|
||||||
|
(days * 86400000) - (weeks * 604800000) - (years * 31536000000)
|
||||||
|
|
||||||
|
// Create a map of the converted duration time.
|
||||||
|
durationMap := map[string]int64{
|
||||||
|
"ms": milliseconds,
|
||||||
|
"s": seconds,
|
||||||
|
"m": minutes,
|
||||||
|
"h": hours,
|
||||||
|
"d": days,
|
||||||
|
"w": weeks,
|
||||||
|
"y": years,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct duration string.
|
||||||
|
for _, u := range [...]string{"y", "w", "d", "h", "m", "s", "ms"} {
|
||||||
|
v := durationMap[u]
|
||||||
|
strval := strconv.FormatInt(v, 10)
|
||||||
|
if v == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
readableString += strval + u
|
||||||
|
}
|
||||||
|
|
||||||
|
return readableString
|
||||||
|
}
|
||||||
|
|
||||||
// Set a Duration
|
// Set a Duration
|
||||||
func (d *Duration) Set(s string) error {
|
func (d *Duration) Set(s string) error {
|
||||||
duration, err := ParseDuration(s)
|
duration, err := ParseDuration(s)
|
||||||
|
|
|
@ -84,6 +84,44 @@ func TestDurationString(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDurationReadableString(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
negative bool
|
||||||
|
in time.Duration
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
// Edge Cases
|
||||||
|
{false, time.Duration(DurationOff), "off"},
|
||||||
|
// Base Cases
|
||||||
|
{false, time.Duration(0), "0s"},
|
||||||
|
{true, time.Millisecond, "1ms"},
|
||||||
|
{true, time.Second, "1s"},
|
||||||
|
{true, time.Minute, "1m"},
|
||||||
|
{true, (3 * time.Minute) / 2, "1m30s"},
|
||||||
|
{true, time.Hour, "1h"},
|
||||||
|
{true, time.Hour * 24, "1d"},
|
||||||
|
{true, time.Hour * 24 * 7, "1w"},
|
||||||
|
{true, time.Hour * 24 * 365, "1y"},
|
||||||
|
// Composite Cases
|
||||||
|
{true, time.Hour + 2*time.Minute + 3*time.Second, "1h2m3s"},
|
||||||
|
{true, time.Hour * 24 * (365 + 14), "1y2w"},
|
||||||
|
{true, time.Hour*24*4 + time.Hour*3 + time.Minute*2 + time.Second, "4d3h2m1s"},
|
||||||
|
{true, time.Hour * 24 * (365*3 + 7*2 + 1), "3y2w1d"},
|
||||||
|
{true, time.Hour*24*(365*3+7*2+1) + time.Hour*2 + time.Second, "3y2w1d2h1s"},
|
||||||
|
{true, time.Hour*24*(365*3+7*2+1) + time.Second, "3y2w1d1s"},
|
||||||
|
{true, time.Hour*24*(365+7*2+3) + time.Hour*4 + time.Minute*5 + time.Second*6 + time.Millisecond*7, "1y2w3d4h5m6s7ms"},
|
||||||
|
} {
|
||||||
|
got := Duration(test.in).ReadableString()
|
||||||
|
assert.Equal(t, test.want, got)
|
||||||
|
|
||||||
|
// Test Negative Case
|
||||||
|
if test.negative {
|
||||||
|
got = Duration(-test.in).ReadableString()
|
||||||
|
assert.Equal(t, "-"+test.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestDurationScan(t *testing.T) {
|
func TestDurationScan(t *testing.T) {
|
||||||
var v Duration
|
var v Duration
|
||||||
n, err := fmt.Sscan(" 17m ", &v)
|
n, err := fmt.Sscan(" 17m ", &v)
|
||||||
|
|
Loading…
Reference in a new issue