mirror of
https://github.com/rclone/rclone.git
synced 2024-11-07 09:04:52 +01:00
accounting: limit length of ETA string
No need to report hours, minutes, and even seconds when the ETA is several years, e.g. "292y24w3d23h47m16s". Now only reports the 3 most significant units, sacrificing precision, e.g. "292y24w3d", "24w3d23h", "3d23h47m", "23h47m16s". Fixes #6381
This commit is contained in:
parent
67132ecaec
commit
0328878e46
@ -269,7 +269,7 @@ func etaString(done, total int64, rate float64) string {
|
||||
if d == etaMax {
|
||||
return "-"
|
||||
}
|
||||
return fs.Duration(d).ReadableString()
|
||||
return fs.Duration(d).ShortReadableString()
|
||||
}
|
||||
|
||||
// percent returns a/b as a percentage rounded to the nearest integer
|
||||
|
@ -29,7 +29,7 @@ func TestETA(t *testing.T) {
|
||||
{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"},
|
||||
{size: 0, total: 95000, rate: 1.0, wantETA: 95000 * time.Second, wantOK: true, wantString: "1d2h23m"}, // Short format, if full it would be "1d2h23m20s"
|
||||
// Standard Duration String Cases
|
||||
{size: 0, total: 1, rate: 2.0, wantETA: 0, wantOK: true, wantString: "0s"},
|
||||
{size: 0, total: 1, rate: 1.0, wantETA: time.Second, wantOK: true, wantString: "1s"},
|
||||
@ -47,7 +47,7 @@ func TestETA(t *testing.T) {
|
||||
// Extreme Cases
|
||||
{size: 0, total: (1 << 63) - 1, rate: 1.0, wantETA: (time.Duration((1<<63)-1) / time.Second) * time.Second, wantOK: true, wantString: "-"},
|
||||
{size: 0, total: ((1 << 63) - 1) / int64(time.Second), rate: 1.0, wantETA: (time.Duration((1<<63)-1) / time.Second) * time.Second, wantOK: true, wantString: "-"},
|
||||
{size: 0, total: ((1<<63)-1)/int64(time.Second) - 1, rate: 1.0, wantETA: (time.Duration((1<<63)-1)/time.Second - 1) * time.Second, wantOK: true, wantString: "292y24w3d23h47m15s"},
|
||||
{size: 0, total: ((1<<63)-1)/int64(time.Second) - 1, rate: 1.0, wantETA: (time.Duration((1<<63)-1)/time.Second - 1) * time.Second, wantOK: true, wantString: "292y24w3d"}, // Short format, if full it would be "292y24w3d23h47m15s"
|
||||
{size: 0, total: ((1<<63)-1)/int64(time.Second) - 1, rate: 0.1, wantETA: (time.Duration((1<<63)-1) / time.Second) * time.Second, wantOK: true, wantString: "-"},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("size=%d/total=%d/rate=%f", test.size, test.total, test.rate), func(t *testing.T) {
|
||||
|
@ -129,9 +129,27 @@ func ParseDuration(age string) (time.Duration, error) {
|
||||
return parseDurationFromNow(age, timeNowFunc)
|
||||
}
|
||||
|
||||
// ReadableString parses d into a human-readable duration.
|
||||
// Based on https://github.com/hako/durafmt
|
||||
// ReadableString parses d into a human-readable duration with units.
|
||||
// Examples: "3s", "1d2h23m20s", "292y24w3d23h47m16s".
|
||||
func (d Duration) ReadableString() string {
|
||||
return d.readableString(0)
|
||||
}
|
||||
|
||||
// ShortReadableString parses d into a human-readable duration with units.
|
||||
// This method returns it in short format, including the 3 most significant
|
||||
// units only, sacrificing precision if necessary. E.g. returns "292y24w3d"
|
||||
// instead of "292y24w3d23h47m16s", and "3d23h47m" instead of "3d23h47m16s".
|
||||
func (d Duration) ShortReadableString() string {
|
||||
return d.readableString(3)
|
||||
}
|
||||
|
||||
// readableString parses d into a human-readable duration with units.
|
||||
// Parameter maxNumberOfUnits limits number of significant units to include,
|
||||
// sacrificing precision. E.g. with argument 3 it returns "292y24w3d" instead
|
||||
// of "292y24w3d23h47m16s", and "3d23h47m" instead of "3d23h47m16s". Zero or
|
||||
// negative argument means include all.
|
||||
// Based on https://github.com/hako/durafmt
|
||||
func (d Duration) readableString(maxNumberOfUnits int) string {
|
||||
switch d {
|
||||
case DurationOff:
|
||||
return "off"
|
||||
@ -179,6 +197,7 @@ func (d Duration) ReadableString() string {
|
||||
}
|
||||
|
||||
// Construct duration string.
|
||||
numberOfUnits := 0
|
||||
for _, u := range [...]string{"y", "w", "d", "h", "m", "s", "ms"} {
|
||||
v := durationMap[u]
|
||||
strval := strconv.FormatInt(v, 10)
|
||||
@ -186,6 +205,10 @@ func (d Duration) ReadableString() string {
|
||||
continue
|
||||
}
|
||||
readableString += strval + u
|
||||
numberOfUnits++
|
||||
if maxNumberOfUnits > 0 && numberOfUnits >= maxNumberOfUnits {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return readableString
|
||||
|
@ -108,38 +108,44 @@ func TestDurationString(t *testing.T) {
|
||||
|
||||
func TestDurationReadableString(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
negative bool
|
||||
in time.Duration
|
||||
want string
|
||||
negative bool
|
||||
in time.Duration
|
||||
wantLong string
|
||||
wantShort string
|
||||
}{
|
||||
// Edge Cases
|
||||
{false, time.Duration(DurationOff), "off"},
|
||||
{false, time.Duration(DurationOff), "off", "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"},
|
||||
{false, time.Duration(0), "0s", "0s"},
|
||||
{true, time.Millisecond, "1ms", "1ms"},
|
||||
{true, time.Second, "1s", "1s"},
|
||||
{true, time.Minute, "1m", "1m"},
|
||||
{true, (3 * time.Minute) / 2, "1m30s", "1m30s"},
|
||||
{true, time.Hour, "1h", "1h"},
|
||||
{true, time.Hour * 24, "1d", "1d"},
|
||||
{true, time.Hour * 24 * 7, "1w", "1w"},
|
||||
{true, time.Hour * 24 * 365, "1y", "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"},
|
||||
{true, time.Hour + 2*time.Minute + 3*time.Second, "1h2m3s", "1h2m3s"},
|
||||
{true, time.Hour * 24 * (365 + 14), "1y2w", "1y2w"},
|
||||
{true, time.Hour*24*4 + time.Hour*3 + time.Minute*2 + time.Second, "4d3h2m1s", "4d3h2m"},
|
||||
{true, time.Hour * 24 * (365*3 + 7*2 + 1), "3y2w1d", "3y2w1d"},
|
||||
{true, time.Hour*24*(365*3+7*2+1) + time.Hour*2 + time.Second, "3y2w1d2h1s", "3y2w1d"},
|
||||
{true, time.Hour*24*(365*3+7*2+1) + time.Second, "3y2w1d1s", "3y2w1d"},
|
||||
{true, time.Hour*24*(365+7*2+3) + time.Hour*4 + time.Minute*5 + time.Second*6 + time.Millisecond*7, "1y2w3d4h5m6s7ms", "1y2w3d"},
|
||||
{true, time.Duration(DurationOff) / time.Millisecond * time.Millisecond, "292y24w3d23h47m16s853ms", "292y24w3d"}, // Should have been 854ms but some precision are lost with floating point calculations
|
||||
} {
|
||||
got := Duration(test.in).ReadableString()
|
||||
assert.Equal(t, test.want, got)
|
||||
assert.Equal(t, test.wantLong, got)
|
||||
got = Duration(test.in).ShortReadableString()
|
||||
assert.Equal(t, test.wantShort, got)
|
||||
|
||||
// Test Negative Case
|
||||
if test.negative {
|
||||
got = Duration(-test.in).ReadableString()
|
||||
assert.Equal(t, "-"+test.want, got)
|
||||
assert.Equal(t, "-"+test.wantLong, got)
|
||||
got = Duration(-test.in).ShortReadableString()
|
||||
assert.Equal(t, "-"+test.wantShort, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user