Skip to content

Commit 8aa9a01

Browse files
amyangfeisiddontang
authored andcommitted
replication: support datetime less than unix timestamp zero (go-mysql-org#409)
* replication: support datetime less than unix timestamp zero
1 parent c884616 commit 8aa9a01

File tree

3 files changed

+57
-0
lines changed

3 files changed

+57
-0
lines changed

replication/row_event.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,19 @@ func decodeDatetime2(data []byte, dec uint16) (interface{}, int, error) {
754754
minute := int((hms >> 6) % (1 << 6))
755755
hour := int((hms >> 12))
756756

757+
// DATETIME encoding for nonfractional part after MySQL 5.6.4
758+
// https://dev.mysql.com/doc/internals/en/date-and-time-data-type-representation.html
759+
// integer value for 1970-01-01 00:00:00 is
760+
// year*13+month = 25611 = 0b110010000001011
761+
// day = 1 = 0b00001
762+
// hour = 0 = 0b00000
763+
// minute = 0 = 0b000000
764+
// second = 0 = 0b000000
765+
// integer value = 0b1100100000010110000100000000000000000 = 107420450816
766+
if intPart < 107420450816 {
767+
return formatBeforeUnixZeroTime(year, month, day, hour, minute, second, int(frac), int(dec)), n, nil
768+
}
769+
757770
return fracTime{
758771
Time: time.Date(year, time.Month(month), day, hour, minute, second, int(frac*1000), time.UTC),
759772
Dec: int(dec),

replication/row_event_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,3 +730,36 @@ func (_ *testDecodeSuite) TestJsonCompatibility(c *C) {
730730
c.Assert(rows.Rows[1][2], DeepEquals, []uint8("{}"))
731731
c.Assert(rows.Rows[2][2], DeepEquals, []uint8("{}"))
732732
}
733+
734+
func (_ *testDecodeSuite) TestDecodeDatetime2(c *C) {
735+
testcases := []struct {
736+
data []byte
737+
dec uint16
738+
getFracTime bool
739+
expected string
740+
}{
741+
{[]byte("\xfe\xf3\xff\x7e\xfb"), 0, true, "9999-12-31 23:59:59"},
742+
{[]byte("\x99\x9a\xb8\xf7\xaa"), 0, true, "2016-10-28 15:30:42"},
743+
{[]byte("\x99\x02\xc2\x00\x00"), 0, true, "1970-01-01 00:00:00"},
744+
{[]byte("\x80\x00\x00\x00\x00"), 0, false, "0000-00-00 00:00:00"},
745+
{[]byte("\x80\x00\x02\xf1\x05"), 0, false, "0000-00-01 15:04:05"},
746+
{[]byte("\x80\x03\x82\x00\x00"), 0, false, "0001-01-01 00:00:00"},
747+
{[]byte("\x80\x03\x82\x00\x00\x0c"), uint16(2), false, "0001-01-01 00:00:00.12"},
748+
{[]byte("\x80\x03\x82\x00\x00\x04\xd3"), uint16(4), false, "0001-01-01 00:00:00.1235"},
749+
{[]byte("\x80\x03\x82\x00\x00\x01\xe2\x40"), uint16(6), false, "0001-01-01 00:00:00.123456"},
750+
}
751+
for _, tc := range testcases {
752+
value, _, err := decodeDatetime2(tc.data, tc.dec)
753+
c.Assert(err, IsNil)
754+
switch t := value.(type) {
755+
case fracTime:
756+
c.Assert(tc.getFracTime, IsTrue)
757+
c.Assert(t.String(), Equals, tc.expected)
758+
case string:
759+
c.Assert(tc.getFracTime, IsFalse)
760+
c.Assert(t, Equals, tc.expected)
761+
default:
762+
c.Errorf("invalid value type: %T", value)
763+
}
764+
}
765+
}

replication/time.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ func formatZeroTime(frac int, dec int) string {
3939
return s[0 : len(s)-(6-dec)]
4040
}
4141

42+
func formatBeforeUnixZeroTime(year, month, day, hour, minute, second, frac, dec int) string {
43+
if dec == 0 {
44+
return fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second)
45+
}
46+
47+
s := fmt.Sprintf("%04d-%02d-%02d %02d:%02d:%02d.%06d", year, month, day, hour, minute, second, frac)
48+
49+
// dec must < 6, if frac is 924000, but dec is 3, we must output 924 here.
50+
return s[0 : len(s)-(6-dec)]
51+
}
52+
4253
func init() {
4354
fracTimeFormat = make([]string, 7)
4455
fracTimeFormat[0] = "2006-01-02 15:04:05"

0 commit comments

Comments
 (0)