123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- // Copyright 2010 The Go Authors. All rights reserved.
-
- package custime
-
- import (
- "errors"
- "time"
- )
-
- // see https://github.com/golang/go/blob/7ad512e7ffe576c4894ea84b02e954846fbda643/src/time/format.go#L1251
-
- // This is a forked version of the ParseDuration function that also handles days/months/years
-
- var errLeadingInt = errors.New("time: 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:]
- }
-
- 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(time.Hour * 24),
- "w": int64(time.Hour * 24 * 7),
- "mo": int64(time.Hour * 24 * 30),
- "y": int64(time.Hour * 24 * 365),
- }
-
- // ParseDuration parses a duration string.
- // 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".
- 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("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)
- }
- 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
- }
-
- type Duration time.Duration
-
- func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
- var orig string
- var err error
- if err = unmarshal(&orig); err != nil {
- return err
- }
- result, err := ParseDuration(orig)
- if err == nil {
- *d = Duration(result)
- }
- return err
- }
|