diff --git a/tstime/tstime.go b/tstime/tstime.go index 93ad3ef52..ff515d535 100644 --- a/tstime/tstime.go +++ b/tstime/tstime.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" - "strconv" "strings" "sync" "time" @@ -105,10 +104,19 @@ func Parse3339(s string) (time.Time, error) { } func parseInt(s string, dst *int) bool { - n, err := strconv.ParseInt(s, 10, 0) - if err != nil || n < 0 { + if len(s) == 0 || len(s) > len("999999999") { + *dst = 0 return false } - *dst = int(n) + n := 0 + for i := 0; i < len(s); i++ { + d := s[i] - '0' + if d > 9 { + *dst = 0 + return false + } + n = n*10 + int(d) + } + *dst = n return true } diff --git a/tstime/tstime_test.go b/tstime/tstime_test.go index 99c5b5441..7f46576e8 100644 --- a/tstime/tstime_test.go +++ b/tstime/tstime_test.go @@ -76,6 +76,30 @@ func TestZoneOf(t *testing.T) { } } +func TestParseInt(t *testing.T) { + tests := []struct { + in string + want int + ret bool + }{ + {"", 0, false}, + {"1", 1, true}, + {"999999999", 999999999, true}, + {"123456789", 123456789, true}, + {"000000", 0, true}, + {"bork", 0, false}, + {"123bork", 0, false}, + } + + for _, tt := range tests { + var got int + gotRet := parseInt(tt.in, &got) + if gotRet != tt.ret || got != tt.want { + t.Errorf("parseInt(%q) = %v, %d; want %v, %d", tt.in, gotRet, got, tt.ret, tt.want) + } + } +} + func BenchmarkGoParse3339(b *testing.B) { run := func(in string) func(*testing.B) { return func(b *testing.B) { @@ -113,6 +137,7 @@ func BenchmarkGoParse3339InLocation(b *testing.B) { b.Fatalf("times don't stringify the same: %q vs %q", s1, s2) } + b.ResetTimer() for i := 0; i < b.N; i++ { _, err := time.ParseInLocation(time.RFC3339Nano, in, loc) if err != nil { @@ -153,3 +178,10 @@ func BenchmarkParse3339(b *testing.B) { b.Run("Z", run("2020-04-05T15:56:00.148487491Z")) b.Run("TZ", run("2020-04-05T15:56:00.148487491+08:00")) } + +func BenchmarkParseInt(b *testing.B) { + var out int + for i := 0; i < b.N; i++ { + parseInt("148487491", &out) + } +}