mirror of
https://github.com/golang/go
synced 2024-11-17 07:45:09 -07: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())
|
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.
|
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
||||||
func (t Time) MarshalBinary() ([]byte, error) {
|
func (t Time) MarshalBinary() ([]byte, error) {
|
||||||
var offsetMin int16 // minutes east of UTC. -1 is UTC.
|
var offsetMin int16 // minutes east of UTC. -1 is UTC.
|
||||||
|
var offsetSec int8
|
||||||
|
version := timeBinaryVersionV1
|
||||||
|
|
||||||
if t.Location() == UTC {
|
if t.Location() == UTC {
|
||||||
offsetMin = -1
|
offsetMin = -1
|
||||||
} else {
|
} else {
|
||||||
_, offset := t.Zone()
|
_, offset := t.Zone()
|
||||||
if offset%60 != 0 {
|
if offset%60 != 0 {
|
||||||
return nil, errors.New("Time.MarshalBinary: zone offset has fractional minute")
|
version = timeBinaryVersionV2
|
||||||
|
offsetSec = int8(offset % 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
offset /= 60
|
offset /= 60
|
||||||
if offset < -32768 || offset == -1 || offset > 32767 {
|
if offset < -32768 || offset == -1 || offset > 32767 {
|
||||||
return nil, errors.New("Time.MarshalBinary: unexpected zone offset")
|
return nil, errors.New("Time.MarshalBinary: unexpected zone offset")
|
||||||
@ -1185,8 +1192,8 @@ func (t Time) MarshalBinary() ([]byte, error) {
|
|||||||
sec := t.sec()
|
sec := t.sec()
|
||||||
nsec := t.nsec()
|
nsec := t.nsec()
|
||||||
enc := []byte{
|
enc := []byte{
|
||||||
timeBinaryVersion, // byte 0 : version
|
version, // byte 0 : version
|
||||||
byte(sec >> 56), // bytes 1-8: seconds
|
byte(sec >> 56), // bytes 1-8: seconds
|
||||||
byte(sec >> 48),
|
byte(sec >> 48),
|
||||||
byte(sec >> 40),
|
byte(sec >> 40),
|
||||||
byte(sec >> 32),
|
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 >> 8), // bytes 13-14: zone offset in minutes
|
||||||
byte(offsetMin),
|
byte(offsetMin),
|
||||||
}
|
}
|
||||||
|
if version == timeBinaryVersionV2 {
|
||||||
|
enc = append(enc, byte(offsetSec))
|
||||||
|
}
|
||||||
|
|
||||||
return enc, nil
|
return enc, nil
|
||||||
}
|
}
|
||||||
@ -1212,11 +1222,16 @@ func (t *Time) UnmarshalBinary(data []byte) error {
|
|||||||
return errors.New("Time.UnmarshalBinary: no data")
|
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")
|
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")
|
return errors.New("Time.UnmarshalBinary: invalid length")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1229,6 +1244,9 @@ func (t *Time) UnmarshalBinary(data []byte) error {
|
|||||||
|
|
||||||
buf = buf[4:]
|
buf = buf[4:]
|
||||||
offset := int(int16(buf[1])|int16(buf[0])<<8) * 60
|
offset := int(int16(buf[1])|int16(buf[0])<<8) * 60
|
||||||
|
if version == timeBinaryVersionV2 {
|
||||||
|
offset += int(buf[2])
|
||||||
|
}
|
||||||
|
|
||||||
*t = Time{}
|
*t = Time{}
|
||||||
t.wall = uint64(nsec)
|
t.wall = uint64(nsec)
|
||||||
|
@ -767,7 +767,6 @@ var notEncodableTimes = []struct {
|
|||||||
time Time
|
time Time
|
||||||
want string
|
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("", -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("", -32769*60)), "Time.MarshalBinary: unexpected zone offset"},
|
||||||
{Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*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
|
// Issue 17720: Zero value of time.Month fails to print
|
||||||
func TestZeroMonthString(t *testing.T) {
|
func TestZeroMonthString(t *testing.T) {
|
||||||
if got, want := Month(0).String(), "%!Month(0)"; got != want {
|
if got, want := Month(0).String(), "%!Month(0)"; got != want {
|
||||||
|
Loading…
Reference in New Issue
Block a user