From d048d29b90a4d534f32c4f906e5f71fe924487fd Mon Sep 17 00:00:00 2001 From: siddontang Date: Mon, 7 Sep 2015 10:20:01 +0800 Subject: [PATCH 1/3] mysqldef: save FSP and zone offset for codec --- mysqldef/time.go | 42 +++++++++++++++++++++++++++++++++++++----- mysqldef/time_test.go | 21 +++++++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/mysqldef/time.go b/mysqldef/time.go index cbf627695866b..c08d1a2658e58 100644 --- a/mysqldef/time.go +++ b/mysqldef/time.go @@ -15,6 +15,7 @@ package mysqldef import ( "bytes" + "encoding/binary" "fmt" "math" "strconv" @@ -137,13 +138,17 @@ func (t Time) IsZero() bool { // Marshal returns the binary encoding of time. func (t Time) Marshal() ([]byte, error) { var ( - b []byte - err error + b []byte + err error + offset int = 0 ) switch t.Type { case TypeDatetime, TypeDate: - _, offset := time.Now().Zone() + // We must use t's Zone not current Now Zone, + // For EDT/EST, even we create the time with time.Local location, + // we may still have a different zone with current Now time. + _, offset = t.Zone() // For datetime and date type, we have a trick to marshal. // e.g, if local time is 2010-10-10T10:10:10 UTC+8 // we will change this to 2010-10-10T10:10:10 UTC and then marshal. @@ -154,7 +159,19 @@ func (t Time) Marshal() ([]byte, error) { err = errors.Errorf("invalid time type %d", t.Type) } - return b, err + if err != nil { + return nil, errors.Trace(err) + } + + // append fsp and offset to the end + b = append(b, byte(t.Fsp)) + b = appendOffset(b, uint32(offset)) + + return b, nil +} + +func appendOffset(b []byte, v uint32) []byte { + return append(b, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) } // Unmarshal decodes the binary data into Time with current local time. @@ -165,6 +182,22 @@ func (t *Time) Unmarshal(b []byte) error { // UnmarshalInLocation decodes the binary data // into Time with a specific time Location. func (t *Time) UnmarshalInLocation(b []byte, loc *time.Location) error { + if len(b) < 5 { + return errors.Errorf("insufficient bytes to unmarshal time") + } + + // Get fsp + fsp, err := checkFsp(int(int8(b[len(b)-5]))) + if err != nil { + return errors.Trace(err) + } + + t.Fsp = fsp + // Get offset + offset := int32(binary.BigEndian.Uint32(b[len(b)-4:])) + + b = b[:len(b)-5] + if err := t.Time.UnmarshalBinary(b); err != nil { return errors.Trace(err) } @@ -175,7 +208,6 @@ func (t *Time) UnmarshalInLocation(b []byte, loc *time.Location) error { if t.Type == TypeDatetime || t.Type == TypeDate { // e.g, for 2010-10-10T10:10:10 UTC, we will unmarshal to 2010-10-10T10:10:10 location - _, offset := time.Now().In(loc).Zone() t.Time = t.Time.Add(-time.Duration(offset) * time.Second).In(loc) if t.Type == TypeDate { // for date type ,we will only use year, month and day. diff --git a/mysqldef/time_test.go b/mysqldef/time_test.go index e69264cb78ad4..c4e158648377e 100644 --- a/mysqldef/time_test.go +++ b/mysqldef/time_test.go @@ -337,6 +337,27 @@ func (s *testTimeSuite) TestCodec(c *C) { err = t4.Unmarshal(b) c.Assert(err, IsNil) c.Assert(t.String(), Equals, t4.String()) + + tbl := []string{ + "2000-01-01 00:00:00.000000", + "2000-01-01 00:00:00.123456", + "0001-01-01 00:00:00.123456", + "2000-06-01 00:00:00.999999", + } + + for _, test := range tbl { + t, err := ParseTime(test, TypeDatetime, MaxFsp) + c.Assert(err, IsNil) + + b, err := t.Marshal() + c.Assert(err, IsNil) + + var dest Time + dest.Type = TypeDatetime + err = dest.Unmarshal(b) + c.Assert(err, IsNil) + c.Assert(dest.String(), Equals, test) + } } func (s *testTimeSuite) TestParseTimeFromNum(c *C) { From fda7a3469fa165fa711ccfc5342587b8c0c62ff4 Mon Sep 17 00:00:00 2001 From: siddontang Date: Mon, 7 Sep 2015 10:43:56 +0800 Subject: [PATCH 2/3] mysqldef: remove offset, create a local time to get zone offset --- mysqldef/time.go | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/mysqldef/time.go b/mysqldef/time.go index c08d1a2658e58..da71c957e0931 100644 --- a/mysqldef/time.go +++ b/mysqldef/time.go @@ -15,7 +15,6 @@ package mysqldef import ( "bytes" - "encoding/binary" "fmt" "math" "strconv" @@ -138,9 +137,8 @@ func (t Time) IsZero() bool { // Marshal returns the binary encoding of time. func (t Time) Marshal() ([]byte, error) { var ( - b []byte - err error - offset int = 0 + b []byte + err error ) switch t.Type { @@ -148,7 +146,7 @@ func (t Time) Marshal() ([]byte, error) { // We must use t's Zone not current Now Zone, // For EDT/EST, even we create the time with time.Local location, // we may still have a different zone with current Now time. - _, offset = t.Zone() + _, offset := t.Zone() // For datetime and date type, we have a trick to marshal. // e.g, if local time is 2010-10-10T10:10:10 UTC+8 // we will change this to 2010-10-10T10:10:10 UTC and then marshal. @@ -163,17 +161,12 @@ func (t Time) Marshal() ([]byte, error) { return nil, errors.Trace(err) } - // append fsp and offset to the end + // append fsp to the end b = append(b, byte(t.Fsp)) - b = appendOffset(b, uint32(offset)) return b, nil } -func appendOffset(b []byte, v uint32) []byte { - return append(b, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) -} - // Unmarshal decodes the binary data into Time with current local time. func (t *Time) Unmarshal(b []byte) error { return t.UnmarshalInLocation(b, time.Local) @@ -182,21 +175,19 @@ func (t *Time) Unmarshal(b []byte) error { // UnmarshalInLocation decodes the binary data // into Time with a specific time Location. func (t *Time) UnmarshalInLocation(b []byte, loc *time.Location) error { - if len(b) < 5 { + if len(b) < 1 { return errors.Errorf("insufficient bytes to unmarshal time") } // Get fsp - fsp, err := checkFsp(int(int8(b[len(b)-5]))) + fsp, err := checkFsp(int(int8(b[len(b)-1]))) if err != nil { return errors.Trace(err) } t.Fsp = fsp - // Get offset - offset := int32(binary.BigEndian.Uint32(b[len(b)-4:])) - b = b[:len(b)-5] + b = b[:len(b)-1] if err := t.Time.UnmarshalBinary(b); err != nil { return errors.Trace(err) @@ -208,10 +199,17 @@ func (t *Time) UnmarshalInLocation(b []byte, loc *time.Location) error { if t.Type == TypeDatetime || t.Type == TypeDate { // e.g, for 2010-10-10T10:10:10 UTC, we will unmarshal to 2010-10-10T10:10:10 location + // We must use Date to creates a time to get offset. Why not time.Now directly, + // because creating time using Date with time.Local may still have a different zone with time.Now. + year, month, day := t.Time.Date() + hour, min, sec := t.Time.Clock() + nsec := t.Time.Nanosecond() + _, offset := time.Date(year, month, day, hour, min, sec, nsec, time.Local).Zone() + t.Time = t.Time.Add(-time.Duration(offset) * time.Second).In(loc) if t.Type == TypeDate { // for date type ,we will only use year, month and day. - year, month, day := t.Time.Date() + year, month, day = t.Time.Date() t.Time = time.Date(year, month, day, 0, 0, 0, 0, loc) } } else if t.Type == TypeTimestamp { From 842721e1d572461347ec0d1f93bfc88f70f4a7dd Mon Sep 17 00:00:00 2001 From: siddontang Date: Mon, 7 Sep 2015 10:50:51 +0800 Subject: [PATCH 3/3] Address comment --- mysqldef/time.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/mysqldef/time.go b/mysqldef/time.go index da71c957e0931..4eb4e341d81b8 100644 --- a/mysqldef/time.go +++ b/mysqldef/time.go @@ -199,17 +199,12 @@ func (t *Time) UnmarshalInLocation(b []byte, loc *time.Location) error { if t.Type == TypeDatetime || t.Type == TypeDate { // e.g, for 2010-10-10T10:10:10 UTC, we will unmarshal to 2010-10-10T10:10:10 location - // We must use Date to creates a time to get offset. Why not time.Now directly, - // because creating time using Date with time.Local may still have a different zone with time.Now. - year, month, day := t.Time.Date() - hour, min, sec := t.Time.Clock() - nsec := t.Time.Nanosecond() - _, offset := time.Date(year, month, day, hour, min, sec, nsec, time.Local).Zone() + _, offset := t.Time.In(loc).Zone() t.Time = t.Time.Add(-time.Duration(offset) * time.Second).In(loc) if t.Type == TypeDate { // for date type ,we will only use year, month and day. - year, month, day = t.Time.Date() + year, month, day := t.Time.Date() t.Time = time.Date(year, month, day, 0, 0, 0, 0, loc) } } else if t.Type == TypeTimestamp {