From bf9570a1b8da44ce104e041e0217d3bb1fc348c8 Mon Sep 17 00:00:00 2001 From: WangXiangUSTC Date: Mon, 1 Apr 2019 12:51:35 +0800 Subject: [PATCH] types: fix incorrect time fraction parsing method (#9933) --- types/time.go | 40 ++++++++++++++++++++++++++++++++++++---- types/time_test.go | 26 ++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/types/time.go b/types/time.go index 8b98bd38b9774..004c7c2bf34ad 100644 --- a/types/time.go +++ b/types/time.go @@ -444,7 +444,13 @@ func (t Time) RoundFrac(sc *stmtctx.StatementContext, fsp int) (Time, error) { // GetFsp gets the fsp of a string. func GetFsp(s string) (fsp int) { - fsp = len(s) - strings.LastIndex(s, ".") - 1 + index := GetFracIndex(s) + if index < 0 { + fsp = 0 + } else { + fsp = len(s) - index - 1 + } + if fsp == len(s) { fsp = 0 } else if fsp > 6 { @@ -453,6 +459,22 @@ func GetFsp(s string) (fsp int) { return } +// GetFracIndex finds the last '.' for get fracStr, index = -1 means fracStr not found. +// but for format like '2019.01.01 00:00:00', the index should be -1. +func GetFracIndex(s string) (index int) { + index = -1 + for i := len(s) - 1; i >= 0; i-- { + if unicode.IsPunct(rune(s[i])) { + if s[i] == '.' { + index = i + } + break + } + } + + return index +} + // RoundFrac rounds fractional seconds precision with new fsp and returns a new one. // We will use the “round half up” rule, e.g, >= 0.5 -> 1, < 0.5 -> 0, // so 2011:11:11 10:10:10.888888 round 0 -> 2011:11:11 10:10:11 @@ -655,9 +677,10 @@ func ParseDateFormat(format string) []string { // See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html. // The only delimiter recognized between a date and time part and a fractional seconds part is the decimal point. func splitDateTime(format string) (seps []string, fracStr string) { - if i := strings.LastIndex(format, "."); i > 0 { - fracStr = strings.TrimSpace(format[i+1:]) - format = format[:i] + index := GetFracIndex(format) + if index > 0 { + fracStr = format[index+1:] + format = format[:index] } seps = ParseDateFormat(format) @@ -742,6 +765,15 @@ func parseDatetime(sc *stmtctx.StatementContext, str string, fsp int, isFloat bo sc.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs("datetime", str)) err = nil } + case 2: + // YYYY-MM is not valid + if len(fracStr) == 0 { + return ZeroDatetime, errors.Trace(ErrIncorrectDatetimeValue.GenWithStackByArgs(str)) + } + + // YYYY-MM.DD, DD is treat as fracStr + err = scanTimeArgs(append(seps, fracStr), &year, &month, &day) + fracStr = "" case 3: // YYYY-MM-DD err = scanTimeArgs(seps, &year, &month, &day) diff --git a/types/time_test.go b/types/time_test.go index 177c6ff6b211e..e7cf071ea23ee 100644 --- a/types/time_test.go +++ b/types/time_test.go @@ -64,6 +64,9 @@ func (s *testTimeSuite) TestDateTime(c *C) { {"170102037.11", "2017-01-02 03:07:11.00"}, {"2018-01-01 18", "2018-01-01 18:00:00"}, {"18-01-01 18", "2018-01-01 18:00:00"}, + {"2018.01.01", "2018-01-01 00:00:00.00"}, + {"2018.01.01 00:00:00", "2018-01-01 00:00:00"}, + {"2018/01/01-00:00:00", "2018-01-01 00:00:00"}, } for _, test := range table { @@ -85,6 +88,9 @@ func (s *testTimeSuite) TestDateTime(c *C) { {"2017-01-05 23:59:59.575601", 0, "2017-01-06 00:00:00"}, {"2017-01-31 23:59:59.575601", 0, "2017-02-01 00:00:00"}, {"2017-00-05 23:59:58.575601", 3, "2017-00-05 23:59:58.576"}, + {"2017.00.05 23:59:58.575601", 3, "2017-00-05 23:59:58.576"}, + {"2017/00/05 23:59:58.575601", 3, "2017-00-05 23:59:58.576"}, + {"2017/00/05-23:59:58.575601", 3, "2017-00-05 23:59:58.576"}, } for _, test := range fspTbl { @@ -105,6 +111,8 @@ func (s *testTimeSuite) TestDateTime(c *C) { "1000-09-31 00:00:00", "1001-02-29 00:00:00", "20170118.999", + "2018-01", + "2018.01", } for _, test := range errTable { @@ -154,6 +162,8 @@ func (s *testTimeSuite) TestDate(c *C) { {"2015-06-01 12:12:12", "2015-06-01"}, {"0001-01-01 00:00:00", "0001-01-01"}, {"0001-01-01", "0001-01-01"}, + {"2019.01.01", "2019-01-01"}, + {"2019/01/01", "2019-01-01"}, } for _, test := range table { @@ -164,6 +174,7 @@ func (s *testTimeSuite) TestDate(c *C) { errTable := []string{ "0121231", + "2019.01", } for _, test := range errTable { @@ -1311,3 +1322,18 @@ func (s *testTimeSuite) TestGetFormatType(c *C) { c.Assert(isDuration, Equals, true) c.Assert(isDate, Equals, false) } + +func (s *testTimeSuite) TestgetFracIndex(c *C) { + testCases := []struct { + str string + expectIndex int + }{ + {"2019.01.01 00:00:00", -1}, + {"2019.01.01 00:00:00.1", 19}, + {"12345.6", 5}, + } + for _, testCase := range testCases { + index := types.GetFracIndex(testCase.str) + c.Assert(index, Equals, testCase.expectIndex) + } +}