Skip to content
1 change: 1 addition & 0 deletions src/time/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ var StdChunkNames = map[int]string{
var Quote = quote

var AppendInt = appendInt
var AppendIntWidth4 = appendIntWidth4
var AppendFormatAny = Time.appendFormat
var AppendFormatRFC3339 = Time.appendFormatRFC3339
var ParseAny = parse
Expand Down
12 changes: 12 additions & 0 deletions src/time/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,18 @@ func appendInt(b []byte, x int, width int) []byte {
return b
}

const onesDigit = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
const tensDigit = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"

// appendIntWidth4 is semantically identical to appendInt(b, x, 4)
// but optimized for 0 ≤ x < 10000.
func appendIntWidth4(b []byte, x int) []byte {
if x < 0 || x >= 1e4 {
return appendInt(b, x, 4)
}
return append(b, tensDigit[x/1e2], onesDigit[x/1e2], tensDigit[x%1e2], onesDigit[x%1e2])
}

// Never printed, just needs to be non-nil for return by atoi.
var errAtoi = errors.New("time: invalid number")

Expand Down
45 changes: 27 additions & 18 deletions src/time/format_rfc3339.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,22 @@ import "errors"
func (t Time) appendFormatRFC3339(b []byte, nanos bool) []byte {
_, offset, abs := t.locabs()

// Format date.
// Format date and time.
year, month, day := abs.days().date()
b = appendInt(b, year, 4)
b = append(b, '-')
b = appendInt(b, int(month), 2)
b = append(b, '-')
b = appendInt(b, day, 2)

b = append(b, 'T')

// Format time.
hour, min, sec := abs.clock()
b = appendInt(b, hour, 2)
b = append(b, ':')
b = appendInt(b, min, 2)
b = append(b, ':')
b = appendInt(b, sec, 2)

b = appendIntWidth4(b, year)
b = append(b, '-',
tensDigit[month], onesDigit[month],
'-',
tensDigit[day], onesDigit[day],
'T',
tensDigit[hour], onesDigit[hour],
':',
tensDigit[min], onesDigit[min],
':',
tensDigit[sec], onesDigit[sec],
)

if nanos {
std := stdFracSecond(stdFracSecond9, 9, '.')
Expand All @@ -53,9 +52,19 @@ func (t Time) appendFormatRFC3339(b []byte, nanos bool) []byte {
} else {
b = append(b, '+')
}
b = appendInt(b, zone/60, 2)
b = append(b, ':')
b = appendInt(b, zone%60, 2)

if zone > 3600 {
b = appendInt(b, zone/60, 2)
b = append(b, ':',
tensDigit[zone%60], onesDigit[zone%60])
return b
}

b = append(b,
tensDigit[zone/60], onesDigit[zone/60],
':',
tensDigit[zone%60], onesDigit[zone%60],
)
return b
}

Expand Down
37 changes: 37 additions & 0 deletions src/time/format_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1091,3 +1091,40 @@ func FuzzParseRFC3339(f *testing.F) {
}
})
}

func TestAppendIntWidth(t *testing.T) {
values := []int{0, -1, 1, 10, -10, 99, -99, 9999, -9999, 10001}
for _, v := range values {
want := AppendInt(nil, v, 4)
got := AppendIntWidth4(nil, v)
if !bytes.Equal(got, want) {
t.Errorf("AppendIntWidth4(%d) = %s, want %s", v, got, want)
}
}
}

func BenchmarkAppendIntWidth4(b *testing.B) {
b.Run("name=AppendInt", func(b *testing.B) {
var buf = make([]byte, 0, 8)
b.ResetTimer()
for b.Loop() {
buf = AppendInt(buf[:0], 360, 4)
}
})
b.Run("name=AppendIntWidth4", func(b *testing.B) {
var buf = make([]byte, 0, 8)
b.ResetTimer()
for b.Loop() {
buf = AppendIntWidth4(buf[:0], 360)
}
})
}

func BenchmarkTimeFormatRFC3339(b *testing.B) {
tm := Unix(1661201140, 676836973)
buf := make([]byte, 0, 64)
b.ReportAllocs()
for b.Loop() {
buf = tm.AppendFormat(buf[:0], RFC3339)
}
}