Skip to content

Commit 819b1b4

Browse files
apocelipesgopherbot
authored andcommitted
time: implement the encoding.(Binary|Text)Appender for Time
"Time.Marshal(Binary|Text)" could also gain some performance improvements. Here is the benchmark highlight: │ old │ new │ │ sec/op │ sec/op vs base │ MarshalText-8 104.00n ± 3% 67.27n ± 2% -35.32% (p=0.000 n=10) MarshalBinary-8 31.77n ± 2% 12.13n ± 1% -61.82% (p=0.000 n=10) geomean 57.48n 28.57n -50.30% │ old │ new │ │ B/op │ B/op vs base │ MarshalText-8 48.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) MarshalBinary-8 16.00 ± 0% 0.00 ± 0% -100.00% (p=0.000 n=10) │ old │ new │ │ allocs/op │ allocs/op vs base │ MarshalText-8 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) MarshalBinary-8 1.000 ± 0% 0.000 ± 0% -100.00% (p=0.000 n=10) For #62384 Change-Id: I320421878a341abf8d668fd57b27292cdfa61330 GitHub-Last-Rev: e04f8df GitHub-Pull-Request: #68942 Reviewed-on: https://go-review.googlesource.com/c/go/+/606655 Reviewed-by: Cherry Mui <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]>
1 parent 1a90dcd commit 819b1b4

File tree

4 files changed

+67
-27
lines changed

4 files changed

+67
-27
lines changed

api/next/62384.txt

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ pkg math/big, method (*Float) AppendText([]uint8) ([]uint8, error) #62384
99
pkg math/big, method (*Int) AppendText([]uint8) ([]uint8, error) #62384
1010
pkg math/big, method (*Rat) AppendText([]uint8) ([]uint8, error) #62384
1111
pkg regexp, method (*Regexp) AppendText([]uint8) ([]uint8, error) #62384
12+
pkg time, method (Time) AppendBinary([]uint8) ([]uint8, error) #62384
13+
pkg time, method (Time) AppendText([]uint8) ([]uint8, error) #62384
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[Time] now implements the [encoding.BinaryAppender] and [encoding.TextAppender] interfaces.

src/time/time.go

+42-27
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,9 @@ import (
119119
// these methods does not change the actual instant it represents, only the time
120120
// zone in which to interpret it.
121121
//
122-
// Representations of a Time value saved by the [Time.GobEncode], [Time.MarshalBinary],
123-
// [Time.MarshalJSON], and [Time.MarshalText] methods store the [Time.Location]'s offset, but not
124-
// the location name. They therefore lose information about Daylight Saving Time.
122+
// Representations of a Time value saved by the [Time.GobEncode], [Time.MarshalBinary], [Time.AppendBinary],
123+
// [Time.MarshalJSON], [Time.MarshalText] and [Time.AppendText] methods store the [Time.Location]'s offset,
124+
// but not the location name. They therefore lose information about Daylight Saving Time.
125125
//
126126
// In addition to the required “wall clock” reading, a Time may contain an optional
127127
// reading of the current process's monotonic clock, to provide additional precision
@@ -1435,8 +1435,8 @@ const (
14351435
timeBinaryVersionV2 // For LMT only
14361436
)
14371437

1438-
// MarshalBinary implements the encoding.BinaryMarshaler interface.
1439-
func (t Time) MarshalBinary() ([]byte, error) {
1438+
// AppendBinary implements the [encoding.BinaryAppender] interface.
1439+
func (t Time) AppendBinary(b []byte) ([]byte, error) {
14401440
var offsetMin int16 // minutes east of UTC. -1 is UTC.
14411441
var offsetSec int8
14421442
version := timeBinaryVersionV1
@@ -1452,38 +1452,46 @@ func (t Time) MarshalBinary() ([]byte, error) {
14521452

14531453
offset /= 60
14541454
if offset < -32768 || offset == -1 || offset > 32767 {
1455-
return nil, errors.New("Time.MarshalBinary: unexpected zone offset")
1455+
return b, errors.New("Time.MarshalBinary: unexpected zone offset")
14561456
}
14571457
offsetMin = int16(offset)
14581458
}
14591459

14601460
sec := t.sec()
14611461
nsec := t.nsec()
1462-
enc := []byte{
1463-
version, // byte 0 : version
1464-
byte(sec >> 56), // bytes 1-8: seconds
1465-
byte(sec >> 48),
1466-
byte(sec >> 40),
1467-
byte(sec >> 32),
1468-
byte(sec >> 24),
1469-
byte(sec >> 16),
1470-
byte(sec >> 8),
1462+
b = append(b,
1463+
version, // byte 0 : version
1464+
byte(sec>>56), // bytes 1-8: seconds
1465+
byte(sec>>48),
1466+
byte(sec>>40),
1467+
byte(sec>>32),
1468+
byte(sec>>24),
1469+
byte(sec>>16),
1470+
byte(sec>>8),
14711471
byte(sec),
1472-
byte(nsec >> 24), // bytes 9-12: nanoseconds
1473-
byte(nsec >> 16),
1474-
byte(nsec >> 8),
1472+
byte(nsec>>24), // bytes 9-12: nanoseconds
1473+
byte(nsec>>16),
1474+
byte(nsec>>8),
14751475
byte(nsec),
1476-
byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes
1476+
byte(offsetMin>>8), // bytes 13-14: zone offset in minutes
14771477
byte(offsetMin),
1478-
}
1478+
)
14791479
if version == timeBinaryVersionV2 {
1480-
enc = append(enc, byte(offsetSec))
1480+
b = append(b, byte(offsetSec))
14811481
}
1482+
return b, nil
1483+
}
14821484

1483-
return enc, nil
1485+
// MarshalBinary implements the [encoding.BinaryMarshaler] interface.
1486+
func (t Time) MarshalBinary() ([]byte, error) {
1487+
b, err := t.AppendBinary(make([]byte, 0, 16))
1488+
if err != nil {
1489+
return nil, err
1490+
}
1491+
return b, nil
14841492
}
14851493

1486-
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
1494+
// UnmarshalBinary implements the [encoding.BinaryUnmarshaler] interface.
14871495
func (t *Time) UnmarshalBinary(data []byte) error {
14881496
buf := data
14891497
if len(buf) == 0 {
@@ -1576,19 +1584,26 @@ func (t *Time) UnmarshalJSON(data []byte) error {
15761584
return err
15771585
}
15781586

1579-
// MarshalText implements the [encoding.TextMarshaler] interface.
1587+
// AppendText implements the [encoding.TextAppender] interface.
15801588
// The time is formatted in RFC 3339 format with sub-second precision.
15811589
// If the timestamp cannot be represented as valid RFC 3339
1582-
// (e.g., the year is out of range), then an error is reported.
1583-
func (t Time) MarshalText() ([]byte, error) {
1584-
b := make([]byte, 0, len(RFC3339Nano))
1590+
// (e.g., the year is out of range), then an error is returned.
1591+
func (t Time) AppendText(b []byte) ([]byte, error) {
15851592
b, err := t.appendStrictRFC3339(b)
15861593
if err != nil {
15871594
return nil, errors.New("Time.MarshalText: " + err.Error())
15881595
}
15891596
return b, nil
15901597
}
15911598

1599+
// MarshalText implements the [encoding.TextMarshaler] interface. The output
1600+
// matches that of calling the [Time.AppendText] method.
1601+
//
1602+
// See [Time.AppendText] for more information.
1603+
func (t Time) MarshalText() ([]byte, error) {
1604+
return t.AppendText(make([]byte, 0, len(RFC3339Nano)))
1605+
}
1606+
15921607
// UnmarshalText implements the [encoding.TextUnmarshaler] interface.
15931608
// The time must be in the RFC 3339 format.
15941609
func (t *Time) UnmarshalText(data []byte) error {

src/time/time_test.go

+22
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,13 @@ var defaultLocTests = []struct {
14031403
{"UnixMilli", func(t1, t2 Time) bool { return t1.UnixMilli() == t2.UnixMilli() }},
14041404
{"UnixMicro", func(t1, t2 Time) bool { return t1.UnixMicro() == t2.UnixMicro() }},
14051405

1406+
{"AppendBinary", func(t1, t2 Time) bool {
1407+
buf1 := make([]byte, 4, 32)
1408+
buf2 := make([]byte, 4, 32)
1409+
a1, b1 := t1.AppendBinary(buf1)
1410+
a2, b2 := t2.AppendBinary(buf2)
1411+
return bytes.Equal(a1[4:], a2[4:]) && b1 == b2
1412+
}},
14061413
{"MarshalBinary", func(t1, t2 Time) bool {
14071414
a1, b1 := t1.MarshalBinary()
14081415
a2, b2 := t2.MarshalBinary()
@@ -1418,6 +1425,14 @@ var defaultLocTests = []struct {
14181425
a2, b2 := t2.MarshalJSON()
14191426
return bytes.Equal(a1, a2) && b1 == b2
14201427
}},
1428+
{"AppendText", func(t1, t2 Time) bool {
1429+
maxCap := len(RFC3339Nano) + 4
1430+
buf1 := make([]byte, 4, maxCap)
1431+
buf2 := make([]byte, 4, maxCap)
1432+
a1, b1 := t1.AppendText(buf1)
1433+
a2, b2 := t2.AppendText(buf2)
1434+
return bytes.Equal(a1[4:], a2[4:]) && b1 == b2
1435+
}},
14211436
{"MarshalText", func(t1, t2 Time) bool {
14221437
a1, b1 := t1.MarshalText()
14231438
a2, b2 := t2.MarshalText()
@@ -1510,6 +1525,13 @@ func BenchmarkMarshalText(b *testing.B) {
15101525
}
15111526
}
15121527

1528+
func BenchmarkMarshalBinary(b *testing.B) {
1529+
t := Now()
1530+
for i := 0; i < b.N; i++ {
1531+
t.MarshalBinary()
1532+
}
1533+
}
1534+
15131535
func BenchmarkParse(b *testing.B) {
15141536
for i := 0; i < b.N; i++ {
15151537
Parse(ANSIC, "Mon Jan 2 15:04:05 2006")

0 commit comments

Comments
 (0)