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.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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. "mo": int64(time.Hour * 24 * 30),
  71. "y": int64(time.Hour * 24 * 265),
  72. }
  73. // ParseDuration parses a duration string.
  74. // A duration string is a possibly signed sequence of
  75. // decimal numbers, each with optional fraction and a unit suffix,
  76. // such as "300ms", "-1.5h" or "2h45m".
  77. // Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
  78. func ParseDuration(s string) (time.Duration, error) {
  79. // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+
  80. orig := s
  81. var d int64
  82. neg := false
  83. // Consume [-+]?
  84. if s != "" {
  85. c := s[0]
  86. if c == '-' || c == '+' {
  87. neg = c == '-'
  88. s = s[1:]
  89. }
  90. }
  91. // Special case: if all that is left is "0", this is zero.
  92. if s == "0" {
  93. return 0, nil
  94. }
  95. if s == "" {
  96. return 0, errors.New("time: invalid duration " + orig)
  97. }
  98. for s != "" {
  99. var (
  100. v, f int64 // integers before, after decimal point
  101. scale float64 = 1 // value = v + f/scale
  102. )
  103. var err error
  104. // The next character must be [0-9.]
  105. if !(s[0] == '.' || '0' <= s[0] && s[0] <= '9') {
  106. return 0, errors.New("time: invalid duration " + orig)
  107. }
  108. // Consume [0-9]*
  109. pl := len(s)
  110. v, s, err = leadingInt(s)
  111. if err != nil {
  112. return 0, errors.New("time: invalid duration " + orig)
  113. }
  114. pre := pl != len(s) // whether we consumed anything before a period
  115. // Consume (\.[0-9]*)?
  116. post := false
  117. if s != "" && s[0] == '.' {
  118. s = s[1:]
  119. pl := len(s)
  120. f, scale, s = leadingFraction(s)
  121. post = pl != len(s)
  122. }
  123. if !pre && !post {
  124. // no digits (e.g. ".s" or "-.s")
  125. return 0, errors.New("time: invalid duration " + orig)
  126. }
  127. // Consume unit.
  128. i := 0
  129. for ; i < len(s); i++ {
  130. c := s[i]
  131. if c == '.' || '0' <= c && c <= '9' {
  132. break
  133. }
  134. }
  135. if i == 0 {
  136. return 0, errors.New("time: missing unit in duration " + orig)
  137. }
  138. u := s[:i]
  139. s = s[i:]
  140. unit, ok := unitMap[u]
  141. if !ok {
  142. return 0, errors.New("time: unknown unit " + u + " in duration " + orig)
  143. }
  144. if v > (1<<63-1)/unit {
  145. // overflow
  146. return 0, errors.New("time: invalid duration " + orig)
  147. }
  148. v *= unit
  149. if f > 0 {
  150. // float64 is needed to be nanosecond accurate for fractions of hours.
  151. // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit)
  152. v += int64(float64(f) * (float64(unit) / scale))
  153. if v < 0 {
  154. // overflow
  155. return 0, errors.New("time: invalid duration " + orig)
  156. }
  157. }
  158. d += v
  159. if d < 0 {
  160. // overflow
  161. return 0, errors.New("time: invalid duration " + orig)
  162. }
  163. }
  164. if neg {
  165. d = -d
  166. }
  167. return time.Duration(d), nil
  168. }