Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make restic find more intuitive by treating --newest as inclusive #3814

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions changelog/unreleased/pull-3814
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Enhancement: Make restic find --newest inclusive

restic find would use the first instant of a date or time passed to `--newest`
as the maximum timestamp for files to list. Now it treats the modification time
as a prefix instead, finding for example all files modified in 2022 for
`--oldest 2022 --newest 2022` instead of only those modified in the first
nanosecond of 2022 (previously `--oldest 2022 --newest 2023` had to be used).

https://github.com/restic/restic/pull/3814
89 changes: 69 additions & 20 deletions cmd/restic/cmd_find.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ func init() {
cmdRoot.AddCommand(cmdFind)

f := cmdFind.Flags()
f.StringVarP(&findOptions.Oldest, "oldest", "O", "", "oldest modification date/time")
f.StringVarP(&findOptions.Newest, "newest", "N", "", "newest modification date/time")
f.StringVarP(&findOptions.Oldest, "oldest", "O", "", "oldest modification date/time (inclusive)")
f.StringVarP(&findOptions.Newest, "newest", "N", "", "newest modification date/time (inclusive)")
f.StringArrayVarP(&findOptions.Snapshots, "snapshot", "s", nil, "snapshot `id` to search in (can be given multiple times)")
f.BoolVar(&findOptions.BlobID, "blob", false, "pattern is a blob-ID")
f.BoolVar(&findOptions.TreeID, "tree", false, "pattern is a tree-ID")
Expand All @@ -83,24 +83,73 @@ type findPattern struct {
ignoreCase bool
}

var timeFormats = []string{
"2006-01-02",
"2006-01-02 15:04",
"2006-01-02 15:04:05",
"2006-01-02 15:04:05 -0700",
"2006-01-02 15:04:05 MST",
"02.01.2006",
"02.01.2006 15:04",
"02.01.2006 15:04:05",
"02.01.2006 15:04:05 -0700",
"02.01.2006 15:04:05 MST",
"Mon Jan 2 15:04:05 -0700 MST 2006",
type inclusiveLayouts struct {
layouts []string
lastInstant func(time.Time) time.Time
}

func parseTime(str string) (time.Time, error) {
for _, fmt := range timeFormats {
if t, err := time.ParseInLocation(fmt, str, time.Local); err == nil {
return t, nil
var timeFormats = []inclusiveLayouts{
inclusiveLayouts{
[]string{
"2006",
},
func(t time.Time) time.Time {
return t.AddDate(1, 0, 0).Add(-1)
},
},
inclusiveLayouts{
[]string{
"2006-01",
"01.2006",
},
func(t time.Time) time.Time {
return t.AddDate(0, 1, 0).Add(-1)
},
},
inclusiveLayouts{
[]string{
"2006-01-02",
"02.01.2006",
},
func(t time.Time) time.Time {
return t.AddDate(0, 0, 1).Add(-1)
},
},
inclusiveLayouts{
[]string{
"2006-01-02 15:04",
"02.01.2006 15:04",
},
func(t time.Time) time.Time {
return t.Add(time.Minute - 1)
},
},
inclusiveLayouts{
[]string{
"2006-01-02 15:04:05",
"2006-01-02 15:04:05 -0700",
"2006-01-02 15:04:05 MST",
"02.01.2006 15:04:05",
"02.01.2006 15:04:05 -0700",
"02.01.2006 15:04:05 MST",
"Mon Jan 2 15:04:05 -0700 MST 2006",
},
func(t time.Time) time.Time {
return t.Add(time.Second - 1)
},
},
}

func parseTime(str string, inclusive bool) (time.Time, error) {
for _, inclusiveLayouts := range timeFormats {
for _, layout := range inclusiveLayouts.layouts {
if t, err := time.ParseInLocation(layout, str, time.Local); err == nil {
if inclusive {
return inclusiveLayouts.lastInstant(t), nil
} else {
return t, nil
}
}
}
}

Expand Down Expand Up @@ -553,13 +602,13 @@ func runFind(opts FindOptions, gopts GlobalOptions, args []string) error {
}

if opts.Oldest != "" {
if pat.oldest, err = parseTime(opts.Oldest); err != nil {
if pat.oldest, err = parseTime(opts.Oldest, false); err != nil {
return err
}
}

if opts.Newest != "" {
if pat.newest, err = parseTime(opts.Newest); err != nil {
if pat.newest, err = parseTime(opts.Newest, true); err != nil {
return err
}
}
Expand Down
4 changes: 2 additions & 2 deletions doc/man/restic-find.1
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ It can also be used to search for restic blobs or trees for troubleshooting.

.PP
\fB\-N\fP, \fB\-\-newest\fP=""
newest modification date/time
newest modification date/time (inclusive)

.PP
\fB\-O\fP, \fB\-\-oldest\fP=""
oldest modification date/time
oldest modification date/time (inclusive)

.PP
\fB\-\-pack\fP[=false]
Expand Down