fs: allow --min-age/--max-age to take a date as well as a duration

Fixes #4211
This commit is contained in:
Nick Craig-Wood 2020-05-11 13:25:39 +01:00
parent 8ddb3fbb2e
commit e91b509578
3 changed files with 65 additions and 13 deletions

View File

@ -429,6 +429,13 @@ seconds or with a suffix of:
For example `--max-age 2d` means no files older than 2 days will be
transferred.
This can also be an absolute time in one of these formats
- RFC3339 - eg "2006-01-02T15:04:05Z07:00"
- ISO8601 Date and time, local timezone - "2006-01-02T15:04:05"
- ISO8601 Date and time, local timezone - "2006-01-02 15:04:05"
- ISO8601 Date - "2006-01-02" (YYYY-MM-DD)
### `--min-age` - Don't transfer any file younger than this ###
This option controls the minimum age of files to transfer. Give in

View File

@ -48,20 +48,10 @@ var ageSuffixes = []struct {
{Suffix: "", Multiplier: time.Second},
}
// ParseDuration parses a duration string. Accept ms|s|m|h|d|w|M|y suffixes. Defaults to second if not provided
func ParseDuration(age string) (time.Duration, error) {
// parse the age as suffixed ages
func parseDurationSuffixes(age string) (time.Duration, error) {
var period float64
if age == "off" {
return time.Duration(DurationOff), nil
}
// Attempt to parse as a time.Duration first
d, err := time.ParseDuration(age)
if err == nil {
return d, nil
}
for _, ageSuffix := range ageSuffixes {
if strings.HasSuffix(age, ageSuffix.Suffix) {
numberString := age[:len(age)-len(ageSuffix.Suffix)]
@ -78,6 +68,51 @@ func ParseDuration(age string) (time.Duration, error) {
return time.Duration(period), nil
}
// time formats to try parsing ages as - in order
var timeFormats = []string{
time.RFC3339,
"2006-01-02T15:04:05",
"2006-01-02 15:04:05",
"2006-01-02",
}
// parse the age as time before the epoch in various date formats
func parseDurationDates(age string, epoch time.Time) (t time.Duration, err error) {
var instant time.Time
for _, timeFormat := range timeFormats {
instant, err = time.Parse(timeFormat, age)
if err == nil {
return epoch.Sub(instant), nil
}
}
return t, err
}
// ParseDuration parses a duration string. Accept ms|s|m|h|d|w|M|y suffixes. Defaults to second if not provided
func ParseDuration(age string) (d time.Duration, err error) {
if age == "off" {
return time.Duration(DurationOff), nil
}
// Attempt to parse as a time.Duration first
d, err = time.ParseDuration(age)
if err == nil {
return d, nil
}
d, err = parseDurationSuffixes(age)
if err == nil {
return d, nil
}
d, err = parseDurationDates(age, time.Now())
if err == nil {
return d, nil
}
return d, err
}
// ReadableString parses d into a human readable duration.
// Based on https://github.com/hako/durafmt
func (d Duration) ReadableString() string {

View File

@ -2,6 +2,7 @@ package fs
import (
"fmt"
"strings"
"testing"
"time"
@ -36,6 +37,10 @@ func TestParseDuration(t *testing.T) {
{"1x", 0, true},
{"off", time.Duration(DurationOff), false},
{"1h2m3s", time.Hour + 2*time.Minute + 3*time.Second, false},
{"2001-02-03", time.Since(time.Date(2001, 2, 3, 0, 0, 0, 0, time.Local)), false},
{"2001-02-03 10:11:12", time.Since(time.Date(2001, 2, 3, 10, 11, 12, 0, time.Local)), false},
{"2001-02-03T10:11:12", time.Since(time.Date(2001, 2, 3, 10, 11, 12, 0, time.Local)), false},
{"2001-02-03T10:11:12.123Z", time.Since(time.Date(2001, 2, 3, 10, 11, 12, 123, time.UTC)), false},
} {
duration, err := ParseDuration(test.in)
if test.err {
@ -43,7 +48,12 @@ func TestParseDuration(t *testing.T) {
} else {
require.NoError(t, err)
}
assert.Equal(t, test.want, duration)
if strings.HasPrefix(test.in, "2001-") {
ok := duration > test.want-time.Second && duration < test.want+time.Second
assert.True(t, ok, test.in)
} else {
assert.Equal(t, test.want, duration)
}
}
}