From 71891b340ccdee5d32a1f8c6371bba8902bf4108 Mon Sep 17 00:00:00 2001
From: plumbeo <plumbeo@users.noreply.github.com>
Date: Wed, 14 Nov 2018 17:24:59 +0100
Subject: [PATCH] Support time ranges expressed in hours in snapshot retention
 policies

Make restic forget --keep-within accept time ranges measured in hours and choose
accordingly which snapshots to keep and which to forget. Add relative tests.
---
 cmd/restic/cmd_forget.go                      |    2 +-
 internal/restic/snapshot_policy.go            |    2 +-
 internal/restic/snapshot_policy_test.go       |    3 +
 .../restic/testdata/policy_keep_snapshots_27  |  150 +++
 .../restic/testdata/policy_keep_snapshots_28  |  374 ++++++
 .../restic/testdata/policy_keep_snapshots_29  | 1132 +++++++++++++++++
 6 files changed, 1661 insertions(+), 2 deletions(-)
 create mode 100644 internal/restic/testdata/policy_keep_snapshots_27
 create mode 100644 internal/restic/testdata/policy_keep_snapshots_28
 create mode 100644 internal/restic/testdata/policy_keep_snapshots_29

diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go
index 3b9862171..bafd540bd 100644
--- a/cmd/restic/cmd_forget.go
+++ b/cmd/restic/cmd_forget.go
@@ -59,7 +59,7 @@ func init() {
 	f.IntVarP(&forgetOptions.Weekly, "keep-weekly", "w", 0, "keep the last `n` weekly snapshots")
 	f.IntVarP(&forgetOptions.Monthly, "keep-monthly", "m", 0, "keep the last `n` monthly snapshots")
 	f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots")
-	f.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that are older than `duration` (eg. 1y5m7d) relative to the latest snapshot")
+	f.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot")
 
 	f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)")
 	f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`")
diff --git a/internal/restic/snapshot_policy.go b/internal/restic/snapshot_policy.go
index 7fc5e384e..08ed843c8 100644
--- a/internal/restic/snapshot_policy.go
+++ b/internal/restic/snapshot_policy.go
@@ -196,7 +196,7 @@ func ApplyPolicy(list Snapshots, p ExpirePolicy) (keep, remove Snapshots, reason
 
 		// If the timestamp of the snapshot is within the range, then keep it.
 		if !p.Within.Zero() {
-			t := latest.AddDate(-p.Within.Years, -p.Within.Months, -p.Within.Days)
+			t := latest.AddDate(-p.Within.Years, -p.Within.Months, -p.Within.Days).Add(time.Hour * time.Duration(-p.Within.Hours))
 			if cur.Time.After(t) {
 				keepSnap = true
 				keepSnapReasons = append(keepSnapReasons, fmt.Sprintf("within %v", p.Within))
diff --git a/internal/restic/snapshot_policy_test.go b/internal/restic/snapshot_policy_test.go
index a9a80dd79..7c9be67e7 100644
--- a/internal/restic/snapshot_policy_test.go
+++ b/internal/restic/snapshot_policy_test.go
@@ -225,6 +225,9 @@ func TestApplyPolicy(t *testing.T) {
 		{Within: parseDuration("1m")},
 		{Within: parseDuration("1m14d")},
 		{Within: parseDuration("1y1d1m")},
+		{Within: parseDuration("13d23h")},
+		{Within: parseDuration("2m2h")},
+		{Within: parseDuration("1y2m3d3h")},
 	}
 
 	for i, p := range tests {
diff --git a/internal/restic/testdata/policy_keep_snapshots_27 b/internal/restic/testdata/policy_keep_snapshots_27
new file mode 100644
index 000000000..a70c49525
--- /dev/null
+++ b/internal/restic/testdata/policy_keep_snapshots_27
@@ -0,0 +1,150 @@
+{
+  "keep": [
+    {
+      "time": "2016-01-18T12:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-12T21:08:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-12T21:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-09T21:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-08T20:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-07T10:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-06T08:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-05T09:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T16:23:03Z",
+      "tree": null,
+      "paths": null
+    }
+  ],
+  "reasons": [
+    {
+      "snapshot": {
+        "time": "2016-01-18T12:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 13d23h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-12T21:08:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 13d23h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-12T21:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 13d23h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-09T21:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 13d23h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-08T20:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 13d23h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-07T10:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 13d23h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-06T08:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 13d23h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-05T09:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 13d23h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T16:23:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 13d23h"
+      ],
+      "counters": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/internal/restic/testdata/policy_keep_snapshots_28 b/internal/restic/testdata/policy_keep_snapshots_28
new file mode 100644
index 000000000..d78171a2f
--- /dev/null
+++ b/internal/restic/testdata/policy_keep_snapshots_28
@@ -0,0 +1,374 @@
+{
+  "keep": [
+    {
+      "time": "2016-01-18T12:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-12T21:08:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-12T21:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-09T21:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-08T20:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-07T10:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-06T08:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-05T09:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T16:23:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T12:30:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T12:28:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T12:24:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T12:23:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T11:23:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T10:23:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-03T07:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-01T07:08:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-01T01:03:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-01T01:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-22T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-21T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-20T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-18T10:20:30Z",
+      "tree": null,
+      "paths": null
+    }
+  ],
+  "reasons": [
+    {
+      "snapshot": {
+        "time": "2016-01-18T12:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-12T21:08:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-12T21:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-09T21:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-08T20:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-07T10:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-06T08:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-05T09:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T16:23:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T12:30:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T12:28:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T12:24:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T12:23:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T11:23:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T10:23:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-03T07:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-01T07:08:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-01T01:03:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-01T01:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-22T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-21T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-20T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-18T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 2m2h"
+      ],
+      "counters": {}
+    }
+  ]
+}
\ No newline at end of file
diff --git a/internal/restic/testdata/policy_keep_snapshots_29 b/internal/restic/testdata/policy_keep_snapshots_29
new file mode 100644
index 000000000..172d3000f
--- /dev/null
+++ b/internal/restic/testdata/policy_keep_snapshots_29
@@ -0,0 +1,1132 @@
+{
+  "keep": [
+    {
+      "time": "2016-01-18T12:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-12T21:08:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-12T21:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-09T21:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-08T20:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-07T10:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-06T08:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-05T09:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T16:23:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T12:30:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T12:28:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T12:24:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T12:23:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T11:23:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-04T10:23:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-03T07:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-01T07:08:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-01T01:03:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2016-01-01T01:02:03Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-22T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-21T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-20T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-18T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-15T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-13T10:20:30.1Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-13T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-12T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-10T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-11-08T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-22T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-22T10:20:30Z",
+      "tree": null,
+      "paths": [
+        "path1",
+        "path2"
+      ],
+      "tags": [
+        "foo",
+        "bar"
+      ]
+    },
+    {
+      "time": "2015-10-22T10:20:30Z",
+      "tree": null,
+      "paths": null,
+      "tags": [
+        "foo",
+        "bar"
+      ]
+    },
+    {
+      "time": "2015-10-22T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-22T10:20:30Z",
+      "tree": null,
+      "paths": null,
+      "tags": [
+        "foo",
+        "bar"
+      ]
+    },
+    {
+      "time": "2015-10-20T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-11T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-10T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-09T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-08T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-06T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-05T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-02T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-10-01T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-09-22T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-09-20T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-09-11T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-09-10T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-09-09T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-09-08T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-09-06T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-09-05T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-09-02T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-09-01T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-08-22T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-08-21T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-08-20T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-08-18T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-08-15T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-08-13T10:20:30.1Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-08-13T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-08-12T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-08-10T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2015-08-08T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2014-11-22T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2014-11-21T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2014-11-20T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2014-11-18T10:20:30Z",
+      "tree": null,
+      "paths": null
+    },
+    {
+      "time": "2014-11-15T10:20:30Z",
+      "tree": null,
+      "paths": null,
+      "tags": [
+        "foo",
+        "bar"
+      ]
+    }
+  ],
+  "reasons": [
+    {
+      "snapshot": {
+        "time": "2016-01-18T12:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-12T21:08:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-12T21:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-09T21:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-08T20:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-07T10:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-06T08:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-05T09:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T16:23:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T12:30:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T12:28:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T12:24:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T12:23:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T11:23:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-04T10:23:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-03T07:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-01T07:08:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-01T01:03:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2016-01-01T01:02:03Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-22T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-21T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-20T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-18T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-15T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-13T10:20:30.1Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-13T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-12T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-10T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-11-08T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-22T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-22T10:20:30Z",
+        "tree": null,
+        "paths": [
+          "path1",
+          "path2"
+        ],
+        "tags": [
+          "foo",
+          "bar"
+        ]
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-22T10:20:30Z",
+        "tree": null,
+        "paths": null,
+        "tags": [
+          "foo",
+          "bar"
+        ]
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-22T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-22T10:20:30Z",
+        "tree": null,
+        "paths": null,
+        "tags": [
+          "foo",
+          "bar"
+        ]
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-20T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-11T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-10T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-09T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-08T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-06T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-05T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-02T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-10-01T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-09-22T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-09-20T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-09-11T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-09-10T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-09-09T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-09-08T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-09-06T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-09-05T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-09-02T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-09-01T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-08-22T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-08-21T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-08-20T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-08-18T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-08-15T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-08-13T10:20:30.1Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-08-13T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-08-12T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-08-10T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2015-08-08T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2014-11-22T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2014-11-21T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2014-11-20T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2014-11-18T10:20:30Z",
+        "tree": null,
+        "paths": null
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    },
+    {
+      "snapshot": {
+        "time": "2014-11-15T10:20:30Z",
+        "tree": null,
+        "paths": null,
+        "tags": [
+          "foo",
+          "bar"
+        ]
+      },
+      "matches": [
+        "within 1y2m3d3h"
+      ],
+      "counters": {}
+    }
+  ]
+}
\ No newline at end of file