123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- package option
- import (
- "errors"
- "time"
- )
- // 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 LICENSE file.
- const durationDay = 24 * time.Hour
- var unitMap = map[string]uint64{
- "ns": uint64(time.Nanosecond),
- "us": uint64(time.Microsecond),
- "µs": uint64(time.Microsecond), // U+00B5 = micro symbol
- "μs": uint64(time.Microsecond), // U+03BC = Greek letter mu
- "ms": uint64(time.Millisecond),
- "s": uint64(time.Second),
- "m": uint64(time.Minute),
- "h": uint64(time.Hour),
- "d": uint64(durationDay),
- }
- // 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) (Duration, error) {
- // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
- orig := s
- var d uint64
- 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 " + quote(orig))
- }
- for s != "" {
- var (
- v, f uint64 // 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 " + quote(orig))
- }
- // Consume [0-9]*
- pl := len(s)
- v, s, err = leadingInt(s)
- if err != nil {
- return 0, errors.New("time: invalid duration " + 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("time: invalid duration " + 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("time: missing unit in duration " + quote(orig))
- }
- u := s[:i]
- s = s[i:]
- unit, ok := unitMap[u]
- if !ok {
- return 0, errors.New("time: unknown unit " + quote(u) + " in duration " + quote(orig))
- }
- if v > 1<<63/unit {
- // overflow
- return 0, errors.New("time: invalid duration " + 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 += uint64(float64(f) * (float64(unit) / scale))
- if v > 1<<63 {
- // overflow
- return 0, errors.New("time: invalid duration " + quote(orig))
- }
- }
- d += v
- if d > 1<<63 {
- return 0, errors.New("time: invalid duration " + quote(orig))
- }
- }
- if neg {
- return -Duration(d), nil
- }
- if d > 1<<63-1 {
- return 0, errors.New("time: invalid duration " + quote(orig))
- }
- return Duration(d), nil
- }
- var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
- // leadingInt consumes the leading [0-9]* from s.
- func leadingInt[bytes []byte | string](s bytes) (x uint64, rem bytes, err error) {
- i := 0
- for ; i < len(s); i++ {
- c := s[i]
- if c < '0' || c > '9' {
- break
- }
- if x > 1<<63/10 {
- // overflow
- return 0, rem, errLeadingInt
- }
- x = x*10 + uint64(c) - '0'
- if x > 1<<63 {
- // overflow
- return 0, rem, 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 uint64, 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 + uint64(c) - '0'
- if y > 1<<63 {
- overflow = true
- continue
- }
- x = y
- scale *= 10
- }
- return x, scale, s[i:]
- }
- // These are borrowed from unicode/utf8 and strconv and replicate behavior in
- // that package, since we can't take a dependency on either.
- const (
- lowerhex = "0123456789abcdef"
- runeSelf = 0x80
- runeError = '\uFFFD'
- )
- func quote(s string) string {
- buf := make([]byte, 1, len(s)+2) // slice will be at least len(s) + quotes
- buf[0] = '"'
- for i, c := range s {
- if c >= runeSelf || c < ' ' {
- // This means you are asking us to parse a time.Duration or
- // time.Location with unprintable or non-ASCII characters in it.
- // We don't expect to hit this case very often. We could try to
- // reproduce strconv.Quote's behavior with full fidelity but
- // given how rarely we expect to hit these edge cases, speed and
- // conciseness are better.
- var width int
- if c == runeError {
- width = 1
- if i+2 < len(s) && s[i:i+3] == string(runeError) {
- width = 3
- }
- } else {
- width = len(string(c))
- }
- for j := 0; j < width; j++ {
- buf = append(buf, `\x`...)
- buf = append(buf, lowerhex[s[i+j]>>4])
- buf = append(buf, lowerhex[s[i+j]&0xF])
- }
- } else {
- if c == '"' || c == '\\' {
- buf = append(buf, '\\')
- }
- buf = append(buf, string(c)...)
- }
- }
- buf = append(buf, '"')
- return string(buf)
- }
|