]> Cypherpunks repositories - gostls13.git/commitdiff
time: support fractional timezone minutes in MarshalBinary
authorHowJMay <vulxj0j8j8@gmail.com>
Thu, 16 Sep 2021 18:22:06 +0000 (18:22 +0000)
committerIan Lance Taylor <iant@golang.org>
Fri, 17 Sep 2021 23:18:47 +0000 (23:18 +0000)
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: 455d7a2496ba67d4a82890b14d57000e1a8a1415
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>

src/time/time.go
src/time/time_test.go

index 1919ebbc2c31f23f80df48aee40c53afbf50c3c6..edf0c6261026ddf6e6599b302870a691f694adca 100644 (file)
@@ -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)
index cea5f2d3f5acebcd18214e0167bf6a2a8de9f5d7..e2fb897b6d6edc72170155e6e39e95a08a89b006 100644 (file)
@@ -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 {