mirror of
https://github.com/golang/go
synced 2024-09-29 12:24:31 -06:00
time: support fractional timezone minutes in MarshalBinary
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
Change-Id: Ib762fb5fa26f54b1a8377a5dde0b994dd5a1236a
GitHub-Last-Rev: 455d7a2496
GitHub-Pull-Request: golang/go#40293
Reviewed-on: https://go-review.googlesource.com/c/go/+/243402
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Carlos Amedee <carlos@golang.org>
This commit is contained in:
parent
07b30a4f77
commit
ac7c34767d
@ -1162,19 +1162,26 @@ func (t Time) UnixNano() int64 {
|
||||
return (t.unixSec())*1e9 + int64(t.nsec())
|
||||
}
|
||||
|
||||
const timeBinaryVersion byte = 1
|
||||
const (
|
||||
timeBinaryVersionV1 byte = iota + 1 // For general situation
|
||||
timeBinaryVersionV2 // For LMT only
|
||||
)
|
||||
|
||||
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
||||
func (t Time) MarshalBinary() ([]byte, error) {
|
||||
var offsetMin int16 // minutes east of UTC. -1 is UTC.
|
||||
var offsetSec int8
|
||||
version := timeBinaryVersionV1
|
||||
|
||||
if t.Location() == UTC {
|
||||
offsetMin = -1
|
||||
} else {
|
||||
_, offset := t.Zone()
|
||||
if offset%60 != 0 {
|
||||
return nil, errors.New("Time.MarshalBinary: zone offset has fractional minute")
|
||||
version = timeBinaryVersionV2
|
||||
offsetSec = int8(offset % 60)
|
||||
}
|
||||
|
||||
offset /= 60
|
||||
if offset < -32768 || offset == -1 || offset > 32767 {
|
||||
return nil, errors.New("Time.MarshalBinary: unexpected zone offset")
|
||||
@ -1185,8 +1192,8 @@ func (t Time) MarshalBinary() ([]byte, error) {
|
||||
sec := t.sec()
|
||||
nsec := t.nsec()
|
||||
enc := []byte{
|
||||
timeBinaryVersion, // byte 0 : version
|
||||
byte(sec >> 56), // bytes 1-8: seconds
|
||||
version, // byte 0 : version
|
||||
byte(sec >> 56), // bytes 1-8: seconds
|
||||
byte(sec >> 48),
|
||||
byte(sec >> 40),
|
||||
byte(sec >> 32),
|
||||
@ -1201,6 +1208,9 @@ func (t Time) MarshalBinary() ([]byte, error) {
|
||||
byte(offsetMin >> 8), // bytes 13-14: zone offset in minutes
|
||||
byte(offsetMin),
|
||||
}
|
||||
if version == timeBinaryVersionV2 {
|
||||
enc = append(enc, byte(offsetSec))
|
||||
}
|
||||
|
||||
return enc, nil
|
||||
}
|
||||
@ -1212,11 +1222,16 @@ func (t *Time) UnmarshalBinary(data []byte) error {
|
||||
return errors.New("Time.UnmarshalBinary: no data")
|
||||
}
|
||||
|
||||
if buf[0] != timeBinaryVersion {
|
||||
version := buf[0]
|
||||
if version != timeBinaryVersionV1 && version != timeBinaryVersionV2 {
|
||||
return errors.New("Time.UnmarshalBinary: unsupported version")
|
||||
}
|
||||
|
||||
if len(buf) != /*version*/ 1+ /*sec*/ 8+ /*nsec*/ 4+ /*zone offset*/ 2 {
|
||||
wantLen := /*version*/ 1 + /*sec*/ 8 + /*nsec*/ 4 + /*zone offset*/ 2
|
||||
if version == timeBinaryVersionV2 {
|
||||
wantLen++
|
||||
}
|
||||
if len(buf) != wantLen {
|
||||
return errors.New("Time.UnmarshalBinary: invalid length")
|
||||
}
|
||||
|
||||
@ -1229,6 +1244,9 @@ func (t *Time) UnmarshalBinary(data []byte) error {
|
||||
|
||||
buf = buf[4:]
|
||||
offset := int(int16(buf[1])|int16(buf[0])<<8) * 60
|
||||
if version == timeBinaryVersionV2 {
|
||||
offset += int(buf[2])
|
||||
}
|
||||
|
||||
*t = Time{}
|
||||
t.wall = uint64(nsec)
|
||||
|
@ -767,7 +767,6 @@ var notEncodableTimes = []struct {
|
||||
time Time
|
||||
want string
|
||||
}{
|
||||
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 1)), "Time.MarshalBinary: zone offset has fractional minute"},
|
||||
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -1*60)), "Time.MarshalBinary: unexpected zone offset"},
|
||||
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -32769*60)), "Time.MarshalBinary: unexpected zone offset"},
|
||||
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*60)), "Time.MarshalBinary: unexpected zone offset"},
|
||||
@ -1437,6 +1436,37 @@ func TestMarshalBinaryZeroTime(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalBinaryVersion2(t *testing.T) {
|
||||
t0, err := Parse(RFC3339, "1880-01-01T00:00:00Z")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to parse time, error = %v", err)
|
||||
}
|
||||
loc, err := LoadLocation("US/Eastern")
|
||||
if err != nil {
|
||||
t.Errorf("Failed to load location, error = %v", err)
|
||||
}
|
||||
t1 := t0.In(loc)
|
||||
b, err := t1.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Errorf("Failed to Marshal, error = %v", err)
|
||||
}
|
||||
|
||||
t2 := Time{}
|
||||
err = t2.UnmarshalBinary(b)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to Unmarshal, error = %v", err)
|
||||
}
|
||||
|
||||
if !(t0.Equal(t1) && t1.Equal(t2)) {
|
||||
if !t0.Equal(t1) {
|
||||
t.Errorf("The result t1: %+v after Marshal is not matched original t0: %+v", t1, t0)
|
||||
}
|
||||
if !t1.Equal(t2) {
|
||||
t.Errorf("The result t2: %+v after Unmarshal is not matched original t1: %+v", t2, t1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 17720: Zero value of time.Month fails to print
|
||||
func TestZeroMonthString(t *testing.T) {
|
||||
if got, want := Month(0).String(), "%!Month(0)"; got != want {
|
||||
|
Loading…
Reference in New Issue
Block a user