Skip to content

Commit c236273

Browse files
committed
src/time: Support LMT in MarshalBinary, UnmarshalBinary
If the time is in 'LMT' and has fractional minute, then `MarshalBinary()` and `UnmarshalBinary()` will encode/decode the time in `timeBinaryVersionV2` in which the fractional minute is at bit 15 and 16, and presented in seconds. Fixes #39616
1 parent dd15017 commit c236273

File tree

2 files changed

+94
-20
lines changed

2 files changed

+94
-20
lines changed

src/time/time.go

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,16 +1138,31 @@ func (t Time) UnixNano() int64 {
11381138
return (t.unixSec())*1e9 + int64(t.nsec())
11391139
}
11401140

1141-
const timeBinaryVersion byte = 1
1141+
const (
1142+
timeBinaryVersionV1 byte = iota + 1 // For general situation
1143+
timeBinaryVersionV2 // For LMT only
1144+
)
1145+
1146+
var timeBinaryVersion byte
11421147

11431148
// MarshalBinary implements the encoding.BinaryMarshaler interface.
11441149
func (t Time) MarshalBinary() ([]byte, error) {
11451150
var offsetMin int16 // minutes east of UTC. -1 is UTC.
1151+
var offsetSec int16
11461152

11471153
if t.Location() == UTC {
11481154
offsetMin = -1
1155+
} else if name, offset := t.Zone(); name == "LMT" {
1156+
timeBinaryVersion = timeBinaryVersionV2
1157+
offsetSec = int16(offset % 60)
1158+
1159+
offset /= 60
1160+
if offset < -32768 || offset == -1 || offset > 32767 {
1161+
return nil, errors.New("Time.MarshalBinary: unexpected zone offset")
1162+
}
1163+
offsetMin = int16(offset)
11491164
} else {
1150-
_, offset := t.Zone()
1165+
timeBinaryVersion = timeBinaryVersionV1
11511166
if offset%60 != 0 {
11521167
return nil, errors.New("Time.MarshalBinary: zone offset has fractional minute")
11531168
}
@@ -1160,22 +1175,47 @@ func (t Time) MarshalBinary() ([]byte, error) {
11601175

11611176
sec := t.sec()
11621177
nsec := t.nsec()
1163-
enc := []byte{
1164-
timeBinaryVersion, // byte 0 : version
1165-
byte(sec >> 56), // bytes 1-8: seconds
1166-
byte(sec >> 48),
1167-
byte(sec >> 40),
1168-
byte(sec >> 32),
1169-
byte(sec >> 24),
1170-
byte(sec >> 16),
1171-
byte(sec >> 8),
1172-
byte(sec),
1173-
byte(nsec >> 24), // bytes 9-12: nanoseconds
1174-
byte(nsec >> 16),
1175-
byte(nsec >> 8),
1176-
byte(nsec),
1177-
byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes
1178-
byte(offsetMin),
1178+
1179+
var enc []byte
1180+
if timeBinaryVersion == timeBinaryVersionV1 {
1181+
enc = []byte{
1182+
timeBinaryVersion, // byte 0 : version
1183+
byte(sec >> 56), // bytes 1-8: seconds
1184+
byte(sec >> 48),
1185+
byte(sec >> 40),
1186+
byte(sec >> 32),
1187+
byte(sec >> 24),
1188+
byte(sec >> 16),
1189+
byte(sec >> 8),
1190+
byte(sec),
1191+
byte(nsec >> 24), // bytes 9-12: nanoseconds
1192+
byte(nsec >> 16),
1193+
byte(nsec >> 8),
1194+
byte(nsec),
1195+
byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes
1196+
byte(offsetMin),
1197+
}
1198+
} else {
1199+
// For timeBinaryVersionV2
1200+
enc = []byte{
1201+
timeBinaryVersion, // byte 0 : version
1202+
byte(sec >> 56), // bytes 1-8: seconds
1203+
byte(sec >> 48),
1204+
byte(sec >> 40),
1205+
byte(sec >> 32),
1206+
byte(sec >> 24),
1207+
byte(sec >> 16),
1208+
byte(sec >> 8),
1209+
byte(sec),
1210+
byte(nsec >> 24), // bytes 9-12: nanoseconds
1211+
byte(nsec >> 16),
1212+
byte(nsec >> 8),
1213+
byte(nsec),
1214+
byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes
1215+
byte(offsetMin),
1216+
byte(offsetSec >> 8), // bytes 15-16: zone offset in seconds which sum with offsetMin can get the total offset time
1217+
byte(offsetSec),
1218+
}
11791219
}
11801220

11811221
return enc, nil
@@ -1191,8 +1231,9 @@ func (t *Time) UnmarshalBinary(data []byte) error {
11911231
if buf[0] != timeBinaryVersion {
11921232
return errors.New("Time.UnmarshalBinary: unsupported version")
11931233
}
1234+
timeVersion := buf[0]
11941235

1195-
if len(buf) != /*version*/ 1+ /*sec*/ 8+ /*nsec*/ 4+ /*zone offset*/ 2 {
1236+
if (len(buf) != /*version*/ 1+ /*sec*/ 8+ /*nsec*/ 4+ /*zone offset*/ 2) && (len(buf) != /*version*/ 1+ /*sec*/ 8+ /*nsec*/ 4+ /*zone offset*/ 4) {
11961237
return errors.New("Time.UnmarshalBinary: invalid length")
11971238
}
11981239

@@ -1204,7 +1245,13 @@ func (t *Time) UnmarshalBinary(data []byte) error {
12041245
nsec := int32(buf[3]) | int32(buf[2])<<8 | int32(buf[1])<<16 | int32(buf[0])<<24
12051246

12061247
buf = buf[4:]
1207-
offset := int(int16(buf[1])|int16(buf[0])<<8) * 60
1248+
var offset int
1249+
if timeVersion == timeBinaryVersionV2 {
1250+
offset = int(int16(buf[1])|int16(buf[0])<<8)*60 + int(int16(buf[3])|int16(buf[2])<<8)
1251+
} else {
1252+
// timeVersion is equal to timeBinaryVersionV1
1253+
offset = int(int16(buf[1])|int16(buf[0])<<8) * 60
1254+
}
12081255

12091256
*t = Time{}
12101257
t.wall = uint64(nsec)

src/time/time_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"sync"
1818
"testing"
1919
"testing/quick"
20+
"time"
2021
. "time"
2122
)
2223

@@ -1394,6 +1395,32 @@ func TestMarshalBinaryZeroTime(t *testing.T) {
13941395
}
13951396
}
13961397

1398+
func TestMarshalBinaryVersion2(t *testing.T) {
1399+
t0, err := time.Parse(time.RFC3339, "1880-01-01T00:00:00Z")
1400+
if err != nil {
1401+
t.Errorf("Failed to parse time, error = %v", err)
1402+
}
1403+
loc, err := time.LoadLocation("US/Eastern")
1404+
if err != nil {
1405+
t.Errorf("Failed to load location, error = %v", err)
1406+
}
1407+
t1 := t0.In(loc)
1408+
b, err := t1.MarshalBinary()
1409+
if err != nil {
1410+
t.Errorf("Failed to Marshal, error = %v", err)
1411+
}
1412+
1413+
t2 := time.Time{}
1414+
err = t2.UnmarshalBinary(b)
1415+
if err != nil {
1416+
t.Errorf("Failed to Unmarshal, error = %v", err)
1417+
}
1418+
1419+
if !(t0.Equal(t1) && t1.Equal(t2)) {
1420+
t.Errorf("The result after Unmarshal is not matched")
1421+
}
1422+
}
1423+
13971424
// Issue 17720: Zero value of time.Month fails to print
13981425
func TestZeroMonthString(t *testing.T) {
13991426
if got, want := Month(0).String(), "%!Month(0)"; got != want {

0 commit comments

Comments
 (0)