diff --git a/src/time/export_test.go b/src/time/export_test.go index a4940d12f91d80..21119561f04570 100644 --- a/src/time/export_test.go +++ b/src/time/export_test.go @@ -134,6 +134,8 @@ var StdChunkNames = map[int]string{ var Quote = quote var AppendInt = appendInt +var AppendIntWidth2 = appendIntWidth2 +var AppendIntWidth4 = appendIntWidth4 var AppendFormatAny = Time.appendFormat var AppendFormatRFC3339 = Time.appendFormatRFC3339 var ParseAny = parse diff --git a/src/time/format.go b/src/time/format.go index ad5486f4d28f89..60c5a3b66e4d9b 100644 --- a/src/time/format.go +++ b/src/time/format.go @@ -464,6 +464,36 @@ func appendInt(b []byte, x int, width int) []byte { return b } +const unitsDigit = "01234567890123456789012345678901234567890123456789" + + "01234567890123456789012345678901234567890123456789" +const tensDigit = "00000000001111111111222222222233333333334444444444" + + "55555555556666666666777777777788888888889999999999" + +// appendIntWidth2 special scenario for appendInt, with parameter width=2 +// Only applicable to integers with absolute value less than 100 +func appendIntWidth2(b []byte, x int) []byte { + if x < 0 { + b = append(b, '-') + x = -x + } + if x >= 1e2 { + x %= 1e2 + } + return append(b, tensDigit[x], unitsDigit[x]) +} + +// appendIntWidth4 special scenario for appendInt, with parameter width=4 +func appendIntWidth4(b []byte, x int) []byte { + if x < 0 { + b = append(b, '-') + x = -x + } + if x >= 1e4 { + return appendInt(b, x, 4) + } + return append(b, tensDigit[x/1e2], unitsDigit[x/1e2], tensDigit[x%1e2], unitsDigit[x%1e2]) +} + // Never printed, just needs to be non-nil for return by atoi. var errAtoi = errors.New("time: invalid number") diff --git a/src/time/format_rfc3339.go b/src/time/format_rfc3339.go index 05fddfca89f64c..deb8dfaa5ce65c 100644 --- a/src/time/format_rfc3339.go +++ b/src/time/format_rfc3339.go @@ -20,21 +20,21 @@ func (t Time) appendFormatRFC3339(b []byte, nanos bool) []byte { // Format date. year, month, day := abs.days().date() - b = appendInt(b, year, 4) + b = appendIntWidth4(b, year) b = append(b, '-') - b = appendInt(b, int(month), 2) + b = appendIntWidth2(b, int(month)) b = append(b, '-') - b = appendInt(b, day, 2) + b = appendIntWidth2(b, day) b = append(b, 'T') // Format time. hour, min, sec := abs.clock() - b = appendInt(b, hour, 2) + b = appendIntWidth2(b, hour) b = append(b, ':') - b = appendInt(b, min, 2) + b = appendIntWidth2(b, min) b = append(b, ':') - b = appendInt(b, sec, 2) + b = appendIntWidth2(b, sec) if nanos { std := stdFracSecond(stdFracSecond9, 9, '.') @@ -53,9 +53,9 @@ func (t Time) appendFormatRFC3339(b []byte, nanos bool) []byte { } else { b = append(b, '+') } - b = appendInt(b, zone/60, 2) + b = appendIntWidth2(b, zone/60) b = append(b, ':') - b = appendInt(b, zone%60, 2) + b = appendIntWidth2(b, zone%60) return b } diff --git a/src/time/format_test.go b/src/time/format_test.go index 2537c765968ee2..b2742544cbebac 100644 --- a/src/time/format_test.go +++ b/src/time/format_test.go @@ -1091,3 +1091,70 @@ func FuzzParseRFC3339(f *testing.F) { } }) } + +func TestAppendIntWidth(t *testing.T) { + values := []int{0, -1, 1, 10, -10, 99, -99} + for _, v := range values { + exp := AppendInt(nil, v, 2) + got := AppendIntWidth2(nil, v) + if !bytes.Equal(got, exp) { + t.Errorf("AppendIntWidth2(%d) = %s, want %s", v, got, exp) + } + } + + got := AppendIntWidth2(nil, 199) + if !bytes.Equal(got, []byte("99")) { + t.Errorf("AppendIntWidth2(199) = %s, want %s", got, []byte("99")) + } + + values = append(values, 9999, -9999, 10001) + for _, v := range values { + exp := AppendInt(nil, v, 4) + got := AppendIntWidth4(nil, v) + if !bytes.Equal(got, exp) { + t.Errorf("AppendIntWidth4(%d) = %s, want %s", v, got, exp) + } + } +} + +func BenchmarkAppendIntWidth2(b *testing.B) { + b.Run("name=AppendInt", func(b *testing.B) { + var buf = make([]byte, 0, 8) + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf = AppendInt(buf[:0], 36, 2) + } + }) + b.Run("name=AppendIntWidth2", func(b *testing.B) { + var buf = make([]byte, 0, 8) + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf = AppendIntWidth2(buf[:0], 36) + } + }) +} + +func BenchmarkAppendIntWidth4(b *testing.B) { + b.Run("name=AppendInt", func(b *testing.B) { + var buf = make([]byte, 0, 8) + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf = AppendInt(buf[:0], 360, 4) + } + }) + b.Run("name=AppendIntWidth4", func(b *testing.B) { + var buf = make([]byte, 0, 8) + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf = AppendIntWidth4(buf[:0], 360) + } + }) +} + +func BenchmarkTimeFormatRFC3339(b *testing.B) { + tm := Now() + buf := make([]byte, 0, 64) + for i := 0; i < b.N; i++ { + buf = tm.AppendFormat(buf[:0], RFC3339) + } +}