Skip to content

Commit f84efec

Browse files
authored
Merge pull request #918 from prometheus/time-split
model: reduce allocations in Time.UnmarshalJSON
2 parents 2269d3d + d6a7988 commit f84efec

2 files changed

Lines changed: 36 additions & 23 deletions

File tree

model/time.go

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -123,44 +123,38 @@ func (t Time) MarshalJSON() ([]byte, error) {
123123

124124
// UnmarshalJSON implements the json.Unmarshaler interface.
125125
func (t *Time) UnmarshalJSON(b []byte) error {
126-
p := strings.Split(string(b), ".")
127-
switch len(p) {
128-
case 1:
129-
v, err := strconv.ParseInt(p[0], 10, 64)
126+
base, frac, found := strings.Cut(string(b), ".")
127+
if !found {
128+
v, err := strconv.ParseInt(base, 10, 64)
130129
if err != nil {
131130
return err
132131
}
133132
*t = Time(v * second)
134-
135-
case 2:
136-
v, err := strconv.ParseInt(p[0], 10, 64)
133+
} else {
134+
v, err := strconv.ParseInt(base, 10, 64)
137135
if err != nil {
138136
return err
139137
}
140-
v *= second
141138

142-
prec := dotPrecision - len(p[1])
139+
prec := dotPrecision - len(frac)
143140
if prec < 0 {
144-
p[1] = p[1][:dotPrecision]
145-
} else if prec > 0 {
146-
p[1] += strings.Repeat("0", prec)
141+
frac = frac[:dotPrecision]
147142
}
148-
149-
va, err := strconv.ParseInt(p[1], 10, 32)
143+
va, err := strconv.ParseInt(frac, 10, 32)
150144
if err != nil {
151145
return err
152146
}
153-
154-
// If the value was something like -0.1 the negative is lost in the
155-
// parsing because of the leading zero, this ensures that we capture it.
156-
if len(p[0]) > 0 && p[0][0] == '-' && v+va > 0 {
157-
*t = Time(v+va) * -1
158-
} else {
159-
*t = Time(v + va)
147+
switch prec {
148+
case 1:
149+
va *= 10
150+
case 2:
151+
va *= 100
160152
}
161153

162-
default:
163-
return fmt.Errorf("invalid time %q", string(b))
154+
if len(base) > 0 && base[0] == '-' {
155+
va = -va
156+
}
157+
*t = Time(v*second + va)
164158
}
165159
return nil
166160
}

model/time_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,11 @@ func TestTimeJSON(t *testing.T) {
432432
}{
433433
{Time(1), `0.001`},
434434
{Time(-1), `-0.001`},
435+
{Time(1001), `1.001`},
436+
{Time(-1001), `-1.001`},
437+
{Time(123000), `123`},
438+
{Time(123100), `123.1`},
439+
{Time(123010), `123.01`},
435440
}
436441

437442
for i, test := range tests {
@@ -460,3 +465,17 @@ func BenchmarkParseDuration(b *testing.B) {
460465
require.NoError(b, err)
461466
}
462467
}
468+
469+
func BenchmarkUnmarshalTime(b *testing.B) {
470+
cases := []string{"1780924784", "1780924784.01", "1780924784.001"}
471+
472+
for _, c := range cases {
473+
b.Run(c, func(b *testing.B) {
474+
var t Time
475+
data := []byte(c)
476+
for b.Loop() {
477+
_ = t.UnmarshalJSON(data)
478+
}
479+
})
480+
}
481+
}

0 commit comments

Comments
 (0)