You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

parseduration.go 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. package custime
  3. import (
  4. "errors"
  5. "time"
  6. )
  7. // see https://github.com/golang/go/blob/7ad512e7ffe576c4894ea84b02e954846fbda643/src/time/format.go#L1251
  8. // This is a forked version of the ParseDuration function that also handles days/months/years
  9. var errLeadingInt = errors.New("time: bad [0-9]*") // never printed
  10. // leadingInt consumes the leading [0-9]* from s.
  11. func leadingInt(s string) (x int64, rem string, err error) {
  12. i := 0
  13. for ; i < len(s); i++ {
  14. c := s[i]
  15. if c < '0' || c > '9' {
  16. break
  17. }
  18. if x > (1<<63-1)/10 {
  19. // overflow
  20. return 0, "", errLeadingInt
  21. }
  22. x = x*10 + int64(c) - '0'
  23. if x < 0 {
  24. // overflow
  25. return 0, "", errLeadingInt
  26. }
  27. }
  28. return x, s[i:], nil
  29. }
  30. // leadingFraction consumes the leading [0-9]* from s.
  31. // It is used only for fractions, so does not return an error on overflow,
  32. // it just stops accumulating precision.
  33. func leadingFraction(s string) (x int64, scale float64, rem string) {
  34. i := 0
  35. scale = 1
  36. overflow := false
  37. for ; i < len(s); i++ {
  38. c := s[i]
  39. if c < '0' || c > '9' {
  40. break
  41. }
  42. if overflow {
  43. continue
  44. }
  45. if x > (1<<63-1)/10 {
  46. // It's possible for overflow to give a positive number, so take care.
  47. overflow = true
  48. continue
  49. }
  50. y := x*10 + int64(c) - '0'
  51. if y < 0 {
  52. overflow = true
  53. continue
  54. }
  55. x = y
  56. scale *= 10
  57. }
  58. return x, scale, s[i:]
  59. }
  60. var unitMap = map[string]int64{
  61. "ns": int64(time.Nanosecond),
  62. "us": int64(time.Microsecond),
  63. "µs": int64(time.Microsecond), // U+00B5 = micro symbol
  64. "μs": int64(time.Microsecond), // U+03BC = Greek letter mu
  65. "ms": int64(time.Millisecond),
  66. "s": int64(time.Second),
  67. "m": int64(time.Minute),
  68. "h": int64(time.Hour),
  69. "d": int64(time.Hour * 24),
  70. "w": int64(time.Hour * 24 * 7),
  71. "mo": int64(time.Hour * 24 * 30),
  72. "y": int64(time.Hour * 24 * 365),
  73. }
  74. // ParseDuration parses a duration string.
  75. // A duration string is a possibly signed sequence of
  76. // decimal numbers, each with optional fraction and a unit suffix,
  77. // such as "300ms", "-1.5h" or "2h45m".
  78. // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
  79. func ParseDuration(s string) (time.Duration, error) {
  80. // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
  81. orig := s
  82. var d int64
  83. neg := false
  84. // Consume [-+]?
  85. if s != "" {
  86. c := s[0]
  87. if c == '-' || c == '+' {
  88. neg = c == '-'
  89. s = s[1:]
  90. }
  91. }
  92. // Special case: if all that is left is "0", this is zero.
  93. if s == "0" {
  94. return 0, nil
  95. }
  96. if s == "" {
  97. return 0, errors.New("time: invalid duration " + orig)
  98. }
  99. for s != "" {
  100. var (
  101. v, f int64 // integers before, after decimal point
  102. scale float64 = 1 // value = v + f/scale
  103. )
  104. var err error
  105. // The next character must be [0-9.]
  106. if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
  107. return 0, errors.New("time: invalid duration " + orig)
  108. }
  109. // Consume [0-9]*
  110. pl := len(s)
  111. v, s, err = leadingInt(s)
  112. if err != nil {
  113. return 0, errors.New("time: invalid duration " + orig)
  114. }
  115. pre := pl != len(s) // whether we consumed anything before a period
  116. // Consume (\.[0-9]*)?
  117. post := false
  118. if s != "" && s[0] == '.' {
  119. s = s[1:]
  120. pl := len(s)
  121. f, scale, s = leadingFraction(s)
  122. post = pl != len(s)
  123. }
  124. if !pre && !post {
  125. // no digits (e.g. ".s" or "-.s")
  126. return 0, errors.New("time: invalid duration " + orig)
  127. }
  128. // Consume unit.
  129. i := 0
  130. for ; i < len(s); i++ {
  131. c := s[i]
  132. if c == '.' || '0' <= c && c <= '9' {
  133. break
  134. }
  135. }
  136. if i == 0 {
  137. return 0, errors.New("time: missing unit in duration " + orig)
  138. }
  139. u := s[:i]
  140. s = s[i:]
  141. unit, ok := unitMap[u]
  142. if !ok {
  143. return 0, errors.New("time: unknown unit " + u + " in duration " + orig)
  144. }
  145. if v > (1<<63-1)/unit {
  146. // overflow
  147. return 0, errors.New("time: invalid duration " + orig)
  148. }
  149. v *= unit
  150. if f > 0 {
  151. // float64 is needed to be nanosecond accurate for fractions of hours.
  152. // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit)
  153. v += int64(float64(f) * (float64(unit) / scale))
  154. if v < 0 {
  155. // overflow
  156. return 0, errors.New("time: invalid duration " + orig)
  157. }
  158. }
  159. d += v
  160. if d < 0 {
  161. // overflow
  162. return 0, errors.New("time: invalid duration " + orig)
  163. }
  164. }
  165. if neg {
  166. d = -d
  167. }
  168. return time.Duration(d), nil
  169. }
  170. type Duration time.Duration
  171. func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
  172. var orig string
  173. var err error
  174. if err = unmarshal(&orig); err != nil {
  175. return err
  176. }
  177. result, err := ParseDuration(orig)
  178. if err == nil {
  179. *d = Duration(result)
  180. }
  181. return err
  182. }