-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add xtime package for jobs/batch from pkg
to avoid circular dependency
- Loading branch information
1 parent
7bf93f4
commit d6e99c2
Showing
5 changed files
with
564 additions
and
1 deletion.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
// Copyright (c) 2015-2024 MinIO, Inc. | ||
// | ||
// This file is part of MinIO Object Storage stack | ||
// | ||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU Affero General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
// | ||
// This program is distributed in the hope that it will be useful | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU Affero General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU Affero General Public License | ||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
package xtime | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/tinylib/msgp/msgp" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
// Additional durations, a day is considered to be 24 hours | ||
const ( | ||
Day time.Duration = time.Hour * 24 | ||
Week = Day * 7 | ||
) | ||
|
||
var unitMap = map[string]int64{ | ||
"ns": int64(time.Nanosecond), | ||
"us": int64(time.Microsecond), | ||
"µs": int64(time.Microsecond), // U+00B5 = micro symbol | ||
"μs": int64(time.Microsecond), // U+03BC = Greek letter mu | ||
"ms": int64(time.Millisecond), | ||
"s": int64(time.Second), | ||
"m": int64(time.Minute), | ||
"h": int64(time.Hour), | ||
"d": int64(Day), | ||
"w": int64(Week), | ||
} | ||
|
||
// ParseDuration parses a duration string. | ||
// The following code is borrowed from time.ParseDuration | ||
// https://cs.opensource.google/go/go/+/refs/tags/go1.22.5:src/time/format.go;l=1589 | ||
// This function extends this function by allowing support for days and weeks. | ||
// This function must only be used when days and weeks are necessary inputs | ||
// in all other cases it is preferred that a user uses Go's time.ParseDuration | ||
func ParseDuration(s string) (time.Duration, error) { | ||
dur, err := time.ParseDuration(s) // Parse via standard Go, if success return right away. | ||
if err == nil { | ||
return dur, nil | ||
} | ||
return parseDuration(s) | ||
} | ||
|
||
// Duration is a wrapper around time.Duration that supports YAML and JSON | ||
type Duration time.Duration | ||
|
||
// D will return as a time.Duration. | ||
func (d Duration) D() time.Duration { | ||
return time.Duration(d) | ||
} | ||
|
||
// UnmarshalYAML implements yaml.Unmarshaler | ||
func (d *Duration) UnmarshalYAML(value *yaml.Node) error { | ||
if value.Kind == yaml.ScalarNode { | ||
dur, err := ParseDuration(value.Value) | ||
if err != nil { | ||
return err | ||
} | ||
*d = Duration(dur) | ||
return nil | ||
} | ||
return fmt.Errorf("unable to unmarshal %s", value.Tag) | ||
} | ||
|
||
// UnmarshalJSON implements json.Unmarshaler | ||
func (d *Duration) UnmarshalJSON(bs []byte) error { | ||
if len(bs) <= 2 { | ||
return nil | ||
} | ||
dur, err := ParseDuration(string(bs[1 : len(bs)-1])) | ||
if err != nil { | ||
return err | ||
} | ||
*d = Duration(dur) | ||
return nil | ||
} | ||
|
||
// MarshalMsg appends the marshaled form of the object to the provided | ||
// byte slice, returning the extended slice and any errors encountered. | ||
func (d Duration) MarshalMsg(bytes []byte) ([]byte, error) { | ||
return msgp.AppendInt64(bytes, int64(d)), nil | ||
} | ||
|
||
// UnmarshalMsg unmarshals the object from binary, | ||
// returing any leftover bytes and any errors encountered. | ||
func (d *Duration) UnmarshalMsg(b []byte) ([]byte, error) { | ||
i, rem, err := msgp.ReadInt64Bytes(b) | ||
*d = Duration(i) | ||
return rem, err | ||
} | ||
|
||
// EncodeMsg writes itself as MessagePack using a *msgp.Writer. | ||
func (d Duration) EncodeMsg(w *msgp.Writer) error { | ||
return w.WriteInt64(int64(d)) | ||
} | ||
|
||
// DecodeMsg decodes itself as MessagePack using a *msgp.Reader. | ||
func (d *Duration) DecodeMsg(reader *msgp.Reader) error { | ||
i, err := reader.ReadInt64() | ||
*d = Duration(i) | ||
return err | ||
} | ||
|
||
// Msgsize returns the maximum serialized size in bytes. | ||
func (d Duration) Msgsize() int { | ||
return msgp.Int64Size | ||
} | ||
|
||
// MarshalYAML implements yaml.Marshaler - Converts duration to human-readable format (e.g., "2h", "30m") | ||
func (d Duration) MarshalYAML() (interface{}, error) { | ||
return time.Duration(d).String(), nil | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
// Copyright 2010 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the go.dev/LICENSE file. | ||
|
||
package xtime | ||
|
||
import ( | ||
"errors" | ||
"strconv" | ||
"time" | ||
) | ||
|
||
// function borrowed from https://cs.opensource.google/go/go/+/refs/tags/go1.22.5:src/time/format.go;l=1589 | ||
// supports days and weeks such as '1d1ms', '1w1ms' | ||
func parseDuration(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("invalid duration " + strconv.Quote(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 " + strconv.Quote(orig)) | ||
} | ||
// Consume [0-9]* | ||
pl := len(s) | ||
v, s, err = leadingInt(s) | ||
if err != nil { | ||
return 0, errors.New("invalid duration " + strconv.Quote(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("invalid duration " + strconv.Quote(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 " + strconv.Quote(orig)) | ||
} | ||
u := s[:i] | ||
s = s[i:] | ||
unit, ok := unitMap[u] | ||
if !ok { | ||
return 0, errors.New("unknown unit " + strconv.Quote(u) + " in duration " + strconv.Quote(orig)) | ||
} | ||
if v > (1<<63-1)/unit { | ||
// overflow | ||
return 0, errors.New("invalid duration " + strconv.Quote(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 " + strconv.Quote(orig)) | ||
} | ||
} | ||
d += v | ||
if d < 0 { | ||
// overflow | ||
return 0, errors.New("invalid duration " + strconv.Quote(orig)) | ||
} | ||
} | ||
|
||
if neg { | ||
d = -d | ||
} | ||
return time.Duration(d), nil | ||
} | ||
|
||
var errLeadingInt = errors.New("bad [0-9]*") // never printed | ||
|
||
// 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:] | ||
} |
Oops, something went wrong.