mirror of
https://github.com/minio/mc
synced 2026-08-29 14:37:42 -04:00
200 lines
4.9 KiB
Go
200 lines
4.9 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the https://go.dev/LICENSE file.
|
|
//
|
|
// Code is borrowed directly from pkg/time.ParseDuration under
|
|
// Go's BSD-style license, extended with changes to support
|
|
// days, weeks and year as time Duration units.
|
|
|
|
// Package cmd provides extended time.Duration implementation
|
|
package cmd
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Duration is a standard unit of time.
|
|
type Duration time.Duration
|
|
|
|
// Days returns the duration as a floating point number of days.
|
|
func (d Duration) Days() float64 {
|
|
hour := d / Hour
|
|
nsec := d % Hour
|
|
return float64(hour) + float64(nsec)*(1e-9/60/60/24)
|
|
}
|
|
|
|
// Standard unit of time.
|
|
var (
|
|
Nanosecond = Duration(time.Nanosecond)
|
|
Microsecond = Duration(time.Microsecond)
|
|
Millisecond = Duration(time.Millisecond)
|
|
Second = Duration(time.Second)
|
|
Minute = Duration(time.Minute)
|
|
Hour = Duration(time.Hour)
|
|
Day = Hour * 24
|
|
Week = Day * 7
|
|
Fortnight = Week * 2
|
|
Month = Day * 30 // Approximation
|
|
Year = Day * 365 // Approximation
|
|
Decade = Year * 10 // Approximation
|
|
Century = Year * 100 // Approximation
|
|
Millennium = Year * 1000 // Approximation
|
|
)
|
|
|
|
// leadingInt consumes the leading [0-9]* from s.
|
|
// this function is directly copied from
|
|
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/time/format.go;drc=55590f3a2b89f001bcadf0df6eb2dde62618302b;l=1455
|
|
// only changes the "error string that's being returned"
|
|
func leadingInt(s string) (x int64, rem string, err error) {
|
|
errLeadingInt := errors.New("bad [0-9]*")
|
|
|
|
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
|
|
}
|
|
|
|
// unitMap defines 'unit' to 'actual-value'
|
|
// translates for calculating duration.
|
|
var unitMap = map[string]int64{
|
|
"ns": int64(Nanosecond),
|
|
"us": int64(Microsecond),
|
|
"µs": int64(Microsecond), // U+00B5 = micro symbol
|
|
"μs": int64(Microsecond), // U+03BC = Greek letter mu
|
|
"ms": int64(Millisecond),
|
|
"s": int64(Second),
|
|
"m": int64(Minute),
|
|
"h": int64(Hour),
|
|
"d": int64(Day),
|
|
"w": int64(Week),
|
|
"y": int64(Year), // Approximation
|
|
}
|
|
|
|
// ParseDuration parses a duration string.
|
|
// This implementation is similar to Go's
|
|
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/time/format.go;l=1546;bpv=1;bpt=1
|
|
// extends it to parse days, weeks and years..
|
|
//
|
|
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h", "d", "w", "y".
|
|
func ParseDuration(s string) (Duration, error) {
|
|
if strings.TrimSpace(s) == "" {
|
|
return 0, errors.New("invalid empty duration")
|
|
}
|
|
|
|
// [-+]?([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("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("invalid duration " + orig)
|
|
}
|
|
// Consume [0-9]*
|
|
pl := len(s)
|
|
v, s, err = leadingInt(s)
|
|
if err != nil {
|
|
return 0, errors.New("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, s, err = leadingInt(s)
|
|
if err != nil {
|
|
return 0, errors.New("invalid duration " + orig)
|
|
}
|
|
for n := pl - len(s); n > 0; n-- {
|
|
scale *= 10
|
|
}
|
|
post = pl != len(s)
|
|
}
|
|
if !pre && !post {
|
|
// no digits (e.g. ".s" or "-.s")
|
|
return 0, errors.New("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("missing unit in duration " + orig)
|
|
}
|
|
u := s[:i]
|
|
s = s[i:]
|
|
unit, ok := unitMap[u]
|
|
if !ok {
|
|
return 0, errors.New("unknown unit " + u + " in duration " + orig)
|
|
}
|
|
if v > (1<<63-1)/unit {
|
|
// overflow
|
|
return 0, errors.New("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("invalid duration " + orig)
|
|
}
|
|
}
|
|
d += v
|
|
if d < 0 {
|
|
// overflow
|
|
return 0, errors.New("invalid duration " + orig)
|
|
}
|
|
}
|
|
|
|
if neg {
|
|
d = -d
|
|
}
|
|
return Duration(d), nil
|
|
}
|