...
1
18
19 package serviceconfig
20
21 import (
22 "encoding/json"
23 "fmt"
24 "math"
25 "strconv"
26 "strings"
27 "time"
28 )
29
30
31
32
33
34 type Duration time.Duration
35
36 func (d Duration) String() string {
37 return fmt.Sprint(time.Duration(d))
38 }
39
40
41 func (d Duration) MarshalJSON() ([]byte, error) {
42 ns := time.Duration(d).Nanoseconds()
43 sec := ns / int64(time.Second)
44 ns = ns % int64(time.Second)
45
46 var sign string
47 if sec < 0 || ns < 0 {
48 sign, sec, ns = "-", -1*sec, -1*ns
49 }
50
51
52
53 str := fmt.Sprintf("%s%d.%09d", sign, sec, ns)
54 str = strings.TrimSuffix(str, "000")
55 str = strings.TrimSuffix(str, "000")
56 str = strings.TrimSuffix(str, ".000")
57 return []byte(fmt.Sprintf("\"%ss\"", str)), nil
58 }
59
60
61 func (d *Duration) UnmarshalJSON(b []byte) error {
62 var s string
63 if err := json.Unmarshal(b, &s); err != nil {
64 return err
65 }
66 if !strings.HasSuffix(s, "s") {
67 return fmt.Errorf("malformed duration %q: missing seconds unit", s)
68 }
69 neg := false
70 if s[0] == '-' {
71 neg = true
72 s = s[1:]
73 }
74 ss := strings.SplitN(s[:len(s)-1], ".", 3)
75 if len(ss) > 2 {
76 return fmt.Errorf("malformed duration %q: too many decimals", s)
77 }
78
79
80 hasDigits := false
81 var sec, ns int64
82 if len(ss[0]) > 0 {
83 var err error
84 if sec, err = strconv.ParseInt(ss[0], 10, 64); err != nil {
85 return fmt.Errorf("malformed duration %q: %v", s, err)
86 }
87
88 const maxProtoSeconds = 315_576_000_000
89 if sec > maxProtoSeconds {
90 return fmt.Errorf("out of range: %q", s)
91 }
92 hasDigits = true
93 }
94 if len(ss) == 2 && len(ss[1]) > 0 {
95 if len(ss[1]) > 9 {
96 return fmt.Errorf("malformed duration %q: too many digits after decimal", s)
97 }
98 var err error
99 if ns, err = strconv.ParseInt(ss[1], 10, 64); err != nil {
100 return fmt.Errorf("malformed duration %q: %v", s, err)
101 }
102 for i := 9; i > len(ss[1]); i-- {
103 ns *= 10
104 }
105 hasDigits = true
106 }
107 if !hasDigits {
108 return fmt.Errorf("malformed duration %q: contains no numbers", s)
109 }
110
111 if neg {
112 sec *= -1
113 ns *= -1
114 }
115
116
117 const maxSeconds = math.MaxInt64 / int64(time.Second)
118 const maxNanosAtMaxSeconds = math.MaxInt64 % int64(time.Second)
119 const minSeconds = math.MinInt64 / int64(time.Second)
120 const minNanosAtMinSeconds = math.MinInt64 % int64(time.Second)
121
122 if sec > maxSeconds || (sec == maxSeconds && ns >= maxNanosAtMaxSeconds) {
123 *d = Duration(math.MaxInt64)
124 } else if sec < minSeconds || (sec == minSeconds && ns <= minNanosAtMinSeconds) {
125 *d = Duration(math.MinInt64)
126 } else {
127 *d = Duration(sec*int64(time.Second) + ns)
128 }
129 return nil
130 }
131
View as plain text