Created
February 18, 2019 15:28
-
-
Save dolftax/c680127e314f35a902ae87d8537f8831 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 65aa0b180b547bce4ed771186e28be10762ad955 Mon Sep 17 00:00:00 2001 | |
From: Ashish <[email protected]> | |
Date: Sat, 24 Nov 2018 12:14:52 +0530 | |
Subject: [PATCH] OlderThan & NewerThan configured for Days, Hours and Minutes | |
Previously OlderThan and NewerThan was configured for day | |
which has been changed to Days, Hours and minutes. | |
--- | |
cmd/cp-main.go | 32 ++-- | |
cmd/mirror-main.go | 32 ++-- | |
cmd/rm-main.go | 38 +++-- | |
cmd/utils.go | 21 ++- | |
docs/minio-client-complete-guide.md | 4 +- | |
pkg/ioutils/format.go | 219 ++++++++++++++++++++++++++++ | |
pkg/ioutils/ioutils_test.go | 39 +++++ | |
7 files changed, 327 insertions(+), 58 deletions(-) | |
create mode 100644 pkg/ioutils/format.go | |
diff --git a/cmd/cp-main.go b/cmd/cp-main.go | |
index 92806b07..5ef6b10b 100644 | |
--- a/cmd/cp-main.go | |
+++ b/cmd/cp-main.go | |
@@ -41,13 +41,13 @@ var ( | |
Name: "recursive, r", | |
Usage: "copy recursively", | |
}, | |
- cli.IntFlag{ | |
+ cli.StringFlag{ | |
Name: "older-than", | |
- Usage: "copy object(s) older than N days", | |
+ Usage: "copy objects older than L days, M hours and N minutes", | |
}, | |
- cli.IntFlag{ | |
+ cli.StringFlag{ | |
Name: "newer-than", | |
- Usage: "copy object(s) newer than N days", | |
+ Usage: "copy objects newer than L days, M hours and N minutes", | |
}, | |
cli.StringFlag{ | |
Name: "storage-class, sc", | |
@@ -93,11 +93,11 @@ EXAMPLES: | |
4. Copy a bucket recursively from aliased Amazon S3 cloud storage to local filesystem on Windows. | |
$ {{.HelpName}} --recursive s3\documents\2014\ C:\Backups\2014 | |
- 5. Copy files older than 7 days from Minio cloud storage to Amazon S3 cloud storage. | |
- $ {{.HelpName}} --older-than 7 play/mybucket/burningman2011/ s3/mybucket/ | |
+ 5. Copy files older than 7 days and 10 hours from Minio cloud storage to Amazon S3 cloud storage. | |
+ $ {{.HelpName}} --older-than "7d10h" play/mybucket/burningman2011/ s3/mybucket/ | |
- 6. Copy files newer than 7 days from Minio cloud storage to a local path. | |
- $ {{.HelpName}} --newer-than 7 play/mybucket/burningman2011/ ~/latest/ | |
+ 6. Copy files newer than 7 days and 10 hours from Minio cloud storage to a local path. | |
+ $ {{.HelpName}} --newer-than "7d10h" play/mybucket/burningman2011/ ~/latest/ | |
7. Copy an object with name containing unicode characters to Amazon S3 cloud storage. | |
$ {{.HelpName}} 本語 s3/andoria/ | |
@@ -217,8 +217,8 @@ func doPrepareCopyURLs(session *sessionV8, trapCh <-chan bool, cancelCopy contex | |
// Access recursive flag inside the session header. | |
isRecursive := session.Header.CommandBoolFlags["recursive"] | |
- olderThan := session.Header.CommandIntFlags["older-than"] | |
- newerThan := session.Header.CommandIntFlags["newer-than"] | |
+ olderThan := session.Header.CommandStringFlags["older-than"] | |
+ newerThan := session.Header.CommandStringFlags["newer-than"] | |
encryptKeys := session.Header.CommandStringFlags["encrypt-key"] | |
encrypt := session.Header.CommandStringFlags["encrypt"] | |
encKeyDB, err := parseAndValidateEncryptionKeys(encryptKeys, encrypt) | |
@@ -260,12 +260,12 @@ func doPrepareCopyURLs(session *sessionV8, trapCh <-chan bool, cancelCopy contex | |
} | |
// Skip objects older than --older-than parameter if specified | |
- if olderThan > 0 && isOlder(cpURLs.SourceContent, olderThan) { | |
+ if olderThan != "" && isOlder(cpURLs.SourceContent, olderThan) { | |
continue | |
} | |
// Skip objects newer than --newer-than parameter if specified | |
- if newerThan > 0 && isNewer(cpURLs.SourceContent, newerThan) { | |
+ if newerThan != "" && isNewer(cpURLs.SourceContent, newerThan) { | |
continue | |
} | |
@@ -451,8 +451,8 @@ func mainCopy(ctx *cli.Context) error { | |
console.SetColor("Copy", color.New(color.FgGreen, color.Bold)) | |
recursive := ctx.Bool("recursive") | |
- olderThan := ctx.Int("older-than") | |
- newerThan := ctx.Int("newer-than") | |
+ olderThan := ctx.String("older-than") | |
+ newerThan := ctx.String("newer-than") | |
storageClass := ctx.String("storage-class") | |
sseKeys := os.Getenv("MC_ENCRYPT_KEY") | |
if key := ctx.String("encrypt-key"); key != "" { | |
@@ -463,8 +463,8 @@ func mainCopy(ctx *cli.Context) error { | |
session := newSessionV8() | |
session.Header.CommandType = "cp" | |
session.Header.CommandBoolFlags["recursive"] = recursive | |
- session.Header.CommandIntFlags["older-than"] = olderThan | |
- session.Header.CommandIntFlags["newer-than"] = newerThan | |
+ session.Header.CommandStringFlags["older-than"] = olderThan | |
+ session.Header.CommandStringFlags["newer-than"] = newerThan | |
session.Header.CommandStringFlags["storage-class"] = storageClass | |
session.Header.CommandStringFlags["encrypt-key"] = sseKeys | |
session.Header.CommandStringFlags["encrypt"] = sse | |
diff --git a/cmd/mirror-main.go b/cmd/mirror-main.go | |
index a9ed3a3a..a680609c 100644 | |
--- a/cmd/mirror-main.go | |
+++ b/cmd/mirror-main.go | |
@@ -71,13 +71,13 @@ var ( | |
Name: "exclude", | |
Usage: "exclude object(s) that match specified object name pattern", | |
}, | |
- cli.IntFlag{ | |
+ cli.StringFlag{ | |
Name: "older-than", | |
- Usage: "filter object(s) older than N days", | |
+ Usage: "filter object(s) older than L days, M hours and N minutes", | |
}, | |
- cli.IntFlag{ | |
+ cli.StringFlag{ | |
Name: "newer-than", | |
- Usage: "filter object(s) newer than N days", | |
+ Usage: "filter object(s) newer than L days, M hours and N minutes", | |
}, | |
cli.StringFlag{ | |
Name: "storage-class, sc", | |
@@ -117,8 +117,8 @@ EXAMPLES: | |
2. Mirror a local folder recursively to Amazon S3 cloud storage. | |
$ {{.HelpName}} backup/ s3/archive | |
- 3. Only mirror files that are newer than 7 days to Amazon S3 cloud storage. | |
- $ {{.HelpName}} --newer-than 7 backup/ s3/archive | |
+ 3. Only mirror files that are newer than 7 days, 10 hours and 30 minutes to Amazon S3 cloud storage. | |
+ $ {{.HelpName}} --newer-than "7d10h30m" backup/ s3/archive | |
4. Mirror a bucket from aliased Amazon S3 cloud storage to a folder on Windows. | |
$ {{.HelpName}} s3\documents\2014\ C:\backup\2014 | |
@@ -139,10 +139,10 @@ EXAMPLES: | |
$ {{.HelpName}} --exclude ".*" --exclude "*.temp" s3/test ~/test | |
9. Mirror objects newer than 10 days from bucket test to a local folder. | |
- $ {{.HelpName}} --newer-than=10 s3/test ~/localfolder | |
+ $ {{.HelpName}} --newer-than=10d s3/test ~/localfolder | |
10. Mirror objects older than 30 days from Amazon S3 bucket test to a local folder. | |
- $ {{.HelpName}} --older-than=30 s3/test ~/test | |
+ $ {{.HelpName}} --older-than=30d s3/test ~/test | |
11. Mirror server encrypted objects from Minio cloud storage to a bucket on Amazon S3 cloud storage | |
$ {{.HelpName}} --encrypt-key "minio/photos=32byteslongsecretkeymustbegiven1,s3/archive=32byteslongsecretkeymustbegiven2" minio/photos/ s3/archive/ | |
@@ -185,7 +185,7 @@ type mirrorJob struct { | |
targetURL string | |
isFake, isRemove, isOverwrite, isWatch bool | |
- olderThan, newerThan int | |
+ olderThan, newerThan string | |
storageClass string | |
excludeOptions []string | |
@@ -245,7 +245,9 @@ func (mj *mirrorJob) doRemove(sURLs URLs) URLs { | |
} | |
} | |
- return sURLs.WithError(nil) | |
+ // Remove extraneous file/bucket on target. | |
+ err := probe.NewError(removeSingle(targetWithAlias, isIncomplete, mj.isFake, "", "", sURLs.encKeyDB)) | |
+ return sURLs.WithError(err) | |
} | |
// doMirror - Mirror an object to multiple destination. URLs status contains a copy of sURLs and error if any. | |
@@ -523,12 +525,12 @@ func (mj *mirrorJob) startMirror(ctx context.Context, cancelMirror context.Cance | |
if sURLs.SourceContent != nil { | |
// Skip objects older than --older-than parameter if specified | |
- if mj.olderThan > 0 && isOlder(sURLs.SourceContent, mj.olderThan) { | |
+ if mj.olderThan != "" && isOlder(sURLs.SourceContent, mj.olderThan) { | |
continue | |
} | |
// Skip objects newer than --newer-than parameter if specified | |
- if mj.newerThan > 0 && isNewer(sURLs.SourceContent, mj.newerThan) { | |
+ if mj.newerThan != "" && isNewer(sURLs.SourceContent, mj.newerThan) { | |
continue | |
} | |
@@ -614,7 +616,7 @@ func (mj *mirrorJob) mirror(ctx context.Context, cancelMirror context.CancelFunc | |
} | |
} | |
-func newMirrorJob(srcURL, dstURL string, isFake, isRemove, isOverwrite, isWatch bool, excludeOptions []string, olderThan, newerThan int, storageClass string, encKeyDB map[string][]prefixSSEPair) *mirrorJob { | |
+func newMirrorJob(srcURL, dstURL string, isFake, isRemove, isOverwrite, isWatch bool, excludeOptions []string, olderThan, newerThan string, storageClass string, encKeyDB map[string][]prefixSSEPair) *mirrorJob { | |
mj := mirrorJob{ | |
trapCh: signalTrap(os.Interrupt, syscall.SIGTERM, syscall.SIGKILL), | |
m: new(sync.Mutex), | |
@@ -691,8 +693,8 @@ func runMirror(srcURL, dstURL string, ctx *cli.Context, encKeyDB map[string][]pr | |
isOverwrite, | |
ctx.Bool("watch"), | |
ctx.StringSlice("exclude"), | |
- ctx.Int("older-than"), | |
- ctx.Int("newer-than"), | |
+ ctx.String("older-than"), | |
+ ctx.String("newer-than"), | |
ctx.String("storage-class"), | |
encKeyDB) | |
diff --git a/cmd/rm-main.go b/cmd/rm-main.go | |
index 274c9696..8e0a7f28 100644 | |
--- a/cmd/rm-main.go | |
+++ b/cmd/rm-main.go | |
@@ -23,7 +23,6 @@ import ( | |
"os" | |
"path/filepath" | |
"strings" | |
- "time" | |
"github.com/fatih/color" | |
"github.com/minio/cli" | |
@@ -31,9 +30,6 @@ import ( | |
"github.com/minio/mc/pkg/probe" | |
) | |
-// Day time.Duration for day. | |
-const Day = 24 * time.Hour | |
- | |
// rm specific flags. | |
var ( | |
rmFlags = []cli.Flag{ | |
@@ -61,13 +57,13 @@ var ( | |
Name: "stdin", | |
Usage: "read object names from STDIN", | |
}, | |
- cli.IntFlag{ | |
+ cli.StringFlag{ | |
Name: "older-than", | |
- Usage: "remove objects older than N days", | |
+ Usage: "remove objects older than L days, M hours and N minutes", | |
}, | |
- cli.IntFlag{ | |
+ cli.StringFlag{ | |
Name: "newer-than", | |
- Usage: "remove objects newer than N days", | |
+ Usage: "remove objects newer than L days, M hours and N minutes", | |
}, | |
} | |
) | |
@@ -99,10 +95,10 @@ EXAMPLES: | |
$ {{.HelpName}} --recursive s3/jazz-songs/louis/ | |
3. Remove all objects older than '90' days recursively from bucket 'jazz-songs' that match 'louis' prefix. | |
- $ {{.HelpName}} --recursive --older-than=90 s3/jazz-songs/louis/ | |
+ $ {{.HelpName}} --recursive --older-than="90d" s3/jazz-songs/louis/ | |
- 4. Remove all objects newer than 7 days recursively from bucket 'pop-songs' | |
- $ {{.HelpName}} --recursive --newer-than=7 s3/pop-songs/ | |
+ 4. Remove all objects newer than 7 days and 10 hours recursively from bucket 'pop-songs' | |
+ $ {{.HelpName}} --recursive --newer-than="7d10h" s3/pop-songs/ | |
5. Remove all objects read from STDIN. | |
$ {{.HelpName}} --force --stdin | |
@@ -110,8 +106,8 @@ EXAMPLES: | |
6. Remove all objects recursively from S3 host | |
$ {{.HelpName}} --recursive --dangerous s3 | |
- 7. Remove all objects older than '90' days recursively from host | |
- $ {{.HelpName}} --recursive --dangerous --older-than=90 s3 | |
+ 7. Remove all buckets and objects older than '90' days recursively from host | |
+ $ {{.HelpName}} --recursive --dangerous --older-than="90d" s3 | |
8. Drop all incomplete uploads on 'jazz-songs' bucket. | |
$ {{.HelpName}} --incomplete --recursive s3/jazz-songs/ | |
@@ -179,7 +175,7 @@ func checkRmSyntax(ctx *cli.Context, encKeyDB map[string][]prefixSSEPair) { | |
} | |
} | |
-func removeSingle(url string, isIncomplete bool, isFake bool, olderThan int, newerThan int, encKeyDB map[string][]prefixSSEPair) error { | |
+func removeSingle(url string, isIncomplete bool, isFake bool, olderThan, newerThan string, encKeyDB map[string][]prefixSSEPair) error { | |
targetAlias, targetURL, _ := mustExpandAlias(url) | |
clnt, pErr := newClientFromAlias(targetAlias, targetURL) | |
if pErr != nil { | |
@@ -196,12 +192,12 @@ func removeSingle(url string, isIncomplete bool, isFake bool, olderThan int, new | |
} | |
// Skip objects older than older--than parameter if specified | |
- if olderThan > 0 && isOlder(content, olderThan) { | |
+ if olderThan != "" && isOlder(content, olderThan) { | |
return nil | |
} | |
// Skip objects older than older--than parameter if specified | |
- if newerThan > 0 && isNewer(content, newerThan) { | |
+ if newerThan != "" && isNewer(content, newerThan) { | |
return nil | |
} | |
@@ -231,7 +227,7 @@ func removeSingle(url string, isIncomplete bool, isFake bool, olderThan int, new | |
return nil | |
} | |
-func removeRecursive(url string, isIncomplete bool, isFake bool, olderThan int, newerThan int, encKeyDB map[string][]prefixSSEPair) error { | |
+func removeRecursive(url string, isIncomplete bool, isFake bool, olderThan, newerThan string, encKeyDB map[string][]prefixSSEPair) error { | |
targetAlias, targetURL, _ := mustExpandAlias(url) | |
clnt, pErr := newClientFromAlias(targetAlias, targetURL) | |
if pErr != nil { | |
@@ -258,12 +254,12 @@ func removeRecursive(url string, isIncomplete bool, isFake bool, olderThan int, | |
urlString := content.URL.Path | |
// Skip objects older than --older-than parameter if specified | |
- if olderThan > 0 && isOlder(content, olderThan) { | |
+ if olderThan != "" && isOlder(content, olderThan) { | |
continue | |
} | |
// Skip objects newer than --newer-than parameter if specified | |
- if newerThan > 0 && isNewer(content, newerThan) { | |
+ if newerThan != "" && isNewer(content, newerThan) { | |
continue | |
} | |
@@ -323,8 +319,8 @@ func mainRm(ctx *cli.Context) error { | |
isRecursive := ctx.Bool("recursive") | |
isFake := ctx.Bool("fake") | |
isStdin := ctx.Bool("stdin") | |
- olderThan := ctx.Int("older-than") | |
- newerThan := ctx.Int("newer-than") | |
+ olderThan := ctx.String("older-than") | |
+ newerThan := ctx.String("newer-than") | |
// Set color. | |
console.SetColor("Remove", color.New(color.FgGreen, color.Bold)) | |
diff --git a/cmd/utils.go b/cmd/utils.go | |
index 71f3891b..9c55d43b 100644 | |
--- a/cmd/utils.go | |
+++ b/cmd/utils.go | |
@@ -31,6 +31,7 @@ import ( | |
"github.com/minio/minio-go/pkg/encrypt" | |
"github.com/minio/mc/pkg/console" | |
+ "github.com/minio/mc/pkg/ioutils" | |
"github.com/minio/mc/pkg/probe" | |
) | |
@@ -162,15 +163,27 @@ func lineTrunc(content string, maxLen int) string { | |
} | |
// isOlder returns true if the passed object is older than olderRef | |
-func isOlder(c *clientContent, olderRef int) bool { | |
+func isOlder(c *clientContent, olderRef string) bool { | |
objectAge := UTCNow().Sub(c.Time) | |
- return objectAge < (time.Duration(olderRef) * Day) | |
+ var e error | |
+ var olderThan time.Duration | |
+ if olderRef != "" { | |
+ olderThan, e = ioutils.ParseDurationTime(olderRef) | |
+ fatalIf(probe.NewError(e), "Unable to parse olderThan=`"+olderRef+"`.") | |
+ } | |
+ return objectAge < olderThan | |
} | |
// isNewer returns true if the passed object is newer than newerRef | |
-func isNewer(c *clientContent, newerRef int) bool { | |
+func isNewer(c *clientContent, newerRef string) bool { | |
objectAge := UTCNow().Sub(c.Time) | |
- return objectAge > (time.Duration(newerRef) * Day) | |
+ var e error | |
+ var newerThan time.Duration | |
+ if newerRef != "" { | |
+ newerThan, e = ioutils.ParseDurationTime(newerRef) | |
+ fatalIf(probe.NewError(e), "Unable to parse newerThan=`"+newerRef+"`.") | |
+ } | |
+ return objectAge > newerThan | |
} | |
// getLookupType returns the minio.BucketLookupType for lookup | |
diff --git a/docs/minio-client-complete-guide.md b/docs/minio-client-complete-guide.md | |
index 8ef32e29..8ae0253b 100644 | |
--- a/docs/minio-client-complete-guide.md | |
+++ b/docs/minio-client-complete-guide.md | |
@@ -566,10 +566,10 @@ Removing `play/mybucket/otherobject.txt`. | |
mc rm --incomplete play/mybucket/myobject.1gig | |
Removing `play/mybucket/myobject.1gig`. | |
``` | |
-*Example: Remove object and output a message only if the object is created older than one day. Otherwise, the command stays quiet and nothing is printed out.* | |
+*Example: Remove object and output a message only if the object is created older than 1 day 2hours and 30 minutes. Otherwise, the command stays quiet and nothing is printed out.* | |
```sh | |
-mc rm -r --force --older-than=1 myminio/mybucket | |
+mc rm -r --force --older-than="1d2h30m" myminio/mybucket | |
Removing `myminio/mybucket/dayOld1.txt`. | |
Removing `myminio/mybucket/dayOld2.txt`. | |
Removing `myminio/mybucket/dayOld3.txt`. | |
diff --git a/pkg/ioutils/format.go b/pkg/ioutils/format.go | |
new file mode 100644 | |
index 00000000..7e167e6b | |
--- /dev/null | |
+++ b/pkg/ioutils/format.go | |
@@ -0,0 +1,219 @@ | |
+/* | |
+ * Minio Cloud Storage, (C) 2018 Minio, Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package ioutils | |
+ | |
+import ( | |
+ "errors" | |
+ "time" | |
+) | |
+ | |
+// A Duration represents the elapsed time between two instants | |
+// as an int64 nanosecond count. The representation limits the | |
+// largest representable duration to approximately 290 years. | |
+type Duration int64 | |
+ | |
+// never printed | |
+var errLeadingInt = errors.New("time: bad [0-9]*") | |
+ | |
+// Common durations. There is no definition for units of Day or larger | |
+// to avoid confusion across daylight savings time zone transitions. | |
+// | |
+// To count the number of units in a Duration, divide: | |
+// second := time.Second | |
+// fmt.Print(int64(second/time.Millisecond)) // prints 1000 | |
+// | |
+// To convert an integer number of units to a Duration, multiply: | |
+// seconds := 10 | |
+// fmt.Print(time.Duration(seconds)*time.Second) // prints 10s | |
+// | |
+const ( | |
+ Nanosecond Duration = 1 | |
+ Microsecond = 1000 * Nanosecond | |
+ Millisecond = 1000 * Microsecond | |
+ Second = 1000 * Millisecond | |
+ Minute = 60 * Second | |
+ Hour = 60 * Minute | |
+ Day = 24 * Hour | |
+) | |
+ | |
+var unitMap = map[string]int64{ | |
+ "ns": int64(Nanosecond), | |
+ "us": int64(Microsecond), | |
+ "ms": int64(Millisecond), | |
+ "s": int64(Second), | |
+ "m": int64(Minute), | |
+ "h": int64(Hour), | |
+ "d": int64(Day), | |
+} | |
+ | |
+// ParseDurationTime parses a duration string in | |
+// the form "10d4h3m". | |
+// A duration string is a possibly signed sequence of | |
+// decimal numbers, each with optional fraction and a unit suffix, | |
+// such as "300ms", "-1.5h" or "2h45m". | |
+// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h", "d". | |
+// It add the days functionality to time.ParseDuration(). | |
+func ParseDurationTime(s string) (time.Duration, error) { | |
+ // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ | |
+ orig := s | |
+ var d int64 | |
+ neg := false | |
+ | |
+ // Consume [-+]? | |
+ if s != "" { | |
+ c := s[0] | |
+ if c == '-' || c == '+' { | |
+ neg = c == '-' | |
+ s = s[1:] | |
+ } | |
+ } | |
+ // Special case: if all that is left is "0", this is zero. | |
+ if s == "0" { | |
+ return 0, nil | |
+ } | |
+ if s == "" { | |
+ return 0, errors.New("time: invalid duration " + orig) | |
+ } | |
+ for s != "" { | |
+ var ( | |
+ v, f int64 // integers before, after decimal point | |
+ scale float64 = 1 // value = v + f/scale | |
+ ) | |
+ | |
+ var err error | |
+ | |
+ // The next character must be [0-9.] | |
+ if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') { | |
+ return 0, errors.New("time: invalid duration " + orig) | |
+ } | |
+ // Consume [0-9]* | |
+ pl := len(s) | |
+ v, s, err = leadingInt(s) | |
+ if err != nil { | |
+ return 0, errors.New("time: invalid duration " + orig) | |
+ } | |
+ pre := pl != len(s) // whether we consumed anything before a period | |
+ | |
+ // Consume (\.[0-9]*)? | |
+ post := false | |
+ if s != "" && s[0] == '.' { | |
+ s = s[1:] | |
+ pl := len(s) | |
+ f, scale, s = leadingFraction(s) | |
+ post = pl != len(s) | |
+ } | |
+ if !pre && !post { | |
+ // no digits (e.g. ".s" or "-.s") | |
+ return 0, errors.New("time: invalid duration " + orig) | |
+ } | |
+ | |
+ // Consume unit. | |
+ i := 0 | |
+ for ; i < len(s); i++ { | |
+ c := s[i] | |
+ if c == '.' || '0' <= c && c <= '9' { | |
+ break | |
+ } | |
+ } | |
+ if i == 0 { | |
+ return 0, errors.New("time: missing unit in duration " + orig + ". Should be of days, hours and minutes format like 7d4h20m ") | |
+ } | |
+ u := s[:i] | |
+ s = s[i:] | |
+ unit, ok := unitMap[u] | |
+ if !ok { | |
+ return 0, errors.New("time: unknown unit " + u + " in duration " + orig) | |
+ } | |
+ if v > (1<<63-1)/unit { | |
+ // overflow | |
+ return 0, errors.New("time: invalid duration " + orig) | |
+ } | |
+ v *= unit | |
+ if f > 0 { | |
+ // float64 is needed to be nanosecond accurate for fractions of hours. | |
+ // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit) | |
+ v += int64(float64(f) * (float64(unit) / scale)) | |
+ if v < 0 { | |
+ // overflow | |
+ return 0, errors.New("time: invalid duration " + orig) | |
+ } | |
+ } | |
+ d += v | |
+ if d < 0 { | |
+ // overflow | |
+ return 0, errors.New("time: invalid duration " + orig) | |
+ } | |
+ } | |
+ | |
+ if neg { | |
+ d = -d | |
+ } | |
+ | |
+ return time.Duration(d), nil | |
+} | |
+ | |
+// leadingInt consumes the leading [0-9]* from s. | |
+func leadingInt(s string) (x int64, rem string, err error) { | |
+ i := 0 | |
+ for ; i < len(s); i++ { | |
+ c := s[i] | |
+ if c < '0' || c > '9' { | |
+ break | |
+ } | |
+ if x > (1<<63-1)/10 { | |
+ // overflow | |
+ return 0, "", errLeadingInt | |
+ } | |
+ x = x*10 + int64(c) - '0' | |
+ if x < 0 { | |
+ // overflow | |
+ return 0, "", errLeadingInt | |
+ } | |
+ } | |
+ return x, s[i:], nil | |
+} | |
+ | |
+// leadingFraction consumes the leading [0-9]* from s. | |
+// It is used only for fractions, so does not return an error on overflow, | |
+// it just stops accumulating precision. | |
+func leadingFraction(s string) (x int64, scale float64, rem string) { | |
+ i := 0 | |
+ scale = 1 | |
+ overflow := false | |
+ for ; i < len(s); i++ { | |
+ c := s[i] | |
+ if c < '0' || c > '9' { | |
+ break | |
+ } | |
+ if overflow { | |
+ continue | |
+ } | |
+ if x > (1<<63-1)/10 { | |
+ // It's possible for overflow to give a positive number, so take care. | |
+ overflow = true | |
+ continue | |
+ } | |
+ y := x*10 + int64(c) - '0' | |
+ if y < 0 { | |
+ overflow = true | |
+ continue | |
+ } | |
+ x = y | |
+ scale *= 10 | |
+ } | |
+ return x, scale, s[i:] | |
+} | |
diff --git a/pkg/ioutils/ioutils_test.go b/pkg/ioutils/ioutils_test.go | |
index beefe5a0..ebe3450e 100644 | |
--- a/pkg/ioutils/ioutils_test.go | |
+++ b/pkg/ioutils/ioutils_test.go | |
@@ -20,6 +20,7 @@ import ( | |
"io/ioutil" | |
"os" | |
"testing" | |
+ "time" | |
"github.com/minio/mc/pkg/ioutils" | |
@@ -41,3 +42,41 @@ func (s *MySuite) TestIoutils(c *C) { | |
c.Assert(err, IsNil) | |
c.Assert(status, Equals, true) | |
} | |
+ | |
+// Test for ParseDurationTime. Validates the returned value | |
+// for given time value in days, hours and minute format. | |
+func TestParseDurationTime(t *testing.T) { | |
+ | |
+ testCases := []struct { | |
+ timeValue string | |
+ expected time.Duration | |
+ err string | |
+ }{ | |
+ // Test 1: empty string as input | |
+ {"", 0, "time: invalid duration "}, | |
+ // Test 2: Input string contains 4 day, 10 hour and 3 minutes | |
+ {"4d10h3m", 381780000000000, ""}, | |
+ // Test 3: Input string contains 10 day and 3 hours | |
+ {"10d3h", 874800000000000, ""}, | |
+ // Test 4: Input string contains minutes and days | |
+ {"3m7d", 604980000000000, ""}, | |
+ // Test 5: Input string contains unknown unit | |
+ {"4a3d", 0, "time: unknown unit a in duration 4a3d"}, | |
+ // Test 6: Input string contains fractional day | |
+ {"1.5d", 129600000000000, ""}, | |
+ // Test 6: Input string contains fractional day and hour | |
+ {"2.5d1.5h", 221400000000000, ""}, | |
+ // Test 7: Input string contains fractional day , hour and minute | |
+ {"2.5d1.5h3.5m", 221610000000000, ""}, | |
+ } | |
+ | |
+ for _, testCase := range testCases { | |
+ myVal, err := ioutils.ParseDurationTime(testCase.timeValue) | |
+ if err != nil && err.Error() != testCase.err { | |
+ t.Error() | |
+ } | |
+ if myVal != testCase.expected { | |
+ t.Error() | |
+ } | |
+ } | |
+} | |
-- | |
2.20.1 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment