From 1cc991276304293cc848b43ad00b215db9240346 Mon Sep 17 00:00:00 2001 From: WangXiangUSTC Date: Wed, 27 Mar 2019 15:28:06 +0800 Subject: [PATCH 1/5] modify parse date time --- types/time.go | 47 ++++++++++++++++++++++++++++++++++++++++++---- types/time_test.go | 8 ++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/types/time.go b/types/time.go index cb96bb917edda..430b7ecd3fbb7 100644 --- a/types/time.go +++ b/types/time.go @@ -399,7 +399,17 @@ 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 + length := len(s) + for i := length - 1; i >= 0; i-- { + if !unicode.IsNumber(rune(s[i])) { + if s[i] == '.' { + fsp = length - i - 1 + } + + break + } + } + if fsp == len(s) { fsp = 0 } else if fsp > 6 { @@ -600,12 +610,25 @@ 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] + // find the last '.' for get fracStr + // but for format like '2019.01.01 00:00:00', the fracStr should be empty + index := 0 + for i := len(format) - 1; i >= 0; i-- { + if !unicode.IsNumber(rune(format[i])) { + if format[i] == '.' { + index = i + } + + break + } } + if index > 0 { + fracStr = format[index+1:] + format = format[:index] + } seps = ParseDateFormat(format) + return } @@ -622,6 +645,10 @@ func parseDatetime(sc *stmtctx.StatementContext, str string, fsp int, isFloat bo ) seps, fracStr := splitDateTime(str) + if err != nil { + return ZeroDatetime, errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(str)) + } + var truncatedOrIncorrect bool switch len(seps) { case 1: @@ -687,6 +714,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) @@ -728,6 +764,9 @@ func parseDatetime(sc *stmtctx.StatementContext, str string, fsp int, isFloat bo return ZeroDatetime, errors.Trace(err) } } + /*else { + fsp = 0 + }*/ tmp := FromDate(year, month, day, hour, minute, second, microsecond) if overflow { diff --git a/types/time_test.go b/types/time_test.go index 2ad3396cef89a..411061761edbc 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 { From a270baa28dc3da7671547c7143c0cb5dac038e45 Mon Sep 17 00:00:00 2001 From: WangXiangUSTC Date: Thu, 28 Mar 2019 15:36:23 +0800 Subject: [PATCH 2/5] minor update --- types/time.go | 54 +++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/types/time.go b/types/time.go index 430b7ecd3fbb7..56825e5b16ffc 100644 --- a/types/time.go +++ b/types/time.go @@ -399,15 +399,11 @@ func (t Time) RoundFrac(sc *stmtctx.StatementContext, fsp int) (Time, error) { // GetFsp gets the fsp of a string. func GetFsp(s string) (fsp int) { - length := len(s) - for i := length - 1; i >= 0; i-- { - if !unicode.IsNumber(rune(s[i])) { - if s[i] == '.' { - fsp = length - i - 1 - } - - break - } + index := getFracIndex(s) + if index < 0 { + fsp = 0 + } else { + fsp = len(s) - getFracIndex(s) - 1 } if fsp == len(s) { @@ -418,6 +414,23 @@ func GetFsp(s string) (fsp int) { return } +// find the last '.' for get fracStr, index = -1 means fracStr not found. +// for format like '2019.01.01 00:00:00', the fracStr should be empty +func getFracIndex(s string) (index int) { + index = -1 + for i := len(s) - 1; i >= 0; i-- { + if !unicode.IsNumber(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 @@ -610,25 +623,13 @@ 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) { - // find the last '.' for get fracStr - // but for format like '2019.01.01 00:00:00', the fracStr should be empty - index := 0 - for i := len(format) - 1; i >= 0; i-- { - if !unicode.IsNumber(rune(format[i])) { - if format[i] == '.' { - index = i - } - - break - } - } - + index := getFracIndex(format) if index > 0 { fracStr = format[index+1:] format = format[:index] } - seps = ParseDateFormat(format) + seps = ParseDateFormat(format) return } @@ -645,10 +646,6 @@ func parseDatetime(sc *stmtctx.StatementContext, str string, fsp int, isFloat bo ) seps, fracStr := splitDateTime(str) - if err != nil { - return ZeroDatetime, errors.Trace(ErrInvalidTimeFormat.GenWithStackByArgs(str)) - } - var truncatedOrIncorrect bool switch len(seps) { case 1: @@ -764,9 +761,6 @@ func parseDatetime(sc *stmtctx.StatementContext, str string, fsp int, isFloat bo return ZeroDatetime, errors.Trace(err) } } - /*else { - fsp = 0 - }*/ tmp := FromDate(year, month, day, hour, minute, second, microsecond) if overflow { From bf972a9434ba810514c33a20d1d02a183b375d2a Mon Sep 17 00:00:00 2001 From: WangXiangUSTC Date: Thu, 28 Mar 2019 15:55:18 +0800 Subject: [PATCH 3/5] add test case --- types/time_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/time_test.go b/types/time_test.go index 411061761edbc..af43375da3402 100644 --- a/types/time_test.go +++ b/types/time_test.go @@ -162,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 { @@ -172,6 +174,7 @@ func (s *testTimeSuite) TestDate(c *C) { errTable := []string{ "0121231", + "2019.01", } for _, test := range errTable { From 69b45e563f83bb94f1fd49105f8c1ae974155576 Mon Sep 17 00:00:00 2001 From: WangXiangUSTC Date: Thu, 28 Mar 2019 16:43:37 +0800 Subject: [PATCH 4/5] minor fix --- types/time.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/time.go b/types/time.go index 56825e5b16ffc..85a324ef5bf75 100644 --- a/types/time.go +++ b/types/time.go @@ -419,7 +419,7 @@ func GetFsp(s string) (fsp int) { func getFracIndex(s string) (index int) { index = -1 for i := len(s) - 1; i >= 0; i-- { - if !unicode.IsNumber(rune(s[i])) { + if unicode.IsPunct(rune(s[i])) { if s[i] == '.' { index = i } From 71661c49673ad52ad92dd6916c2c6abdb8a0a460 Mon Sep 17 00:00:00 2001 From: WangXiangUSTC Date: Mon, 1 Apr 2019 11:25:21 +0800 Subject: [PATCH 5/5] add unit test && address comment --- types/time.go | 13 ++++++------- types/time_test.go | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/types/time.go b/types/time.go index 85a324ef5bf75..82dbb56fe05b9 100644 --- a/types/time.go +++ b/types/time.go @@ -399,11 +399,11 @@ func (t Time) RoundFrac(sc *stmtctx.StatementContext, fsp int) (Time, error) { // GetFsp gets the fsp of a string. func GetFsp(s string) (fsp int) { - index := getFracIndex(s) + index := GetFracIndex(s) if index < 0 { fsp = 0 } else { - fsp = len(s) - getFracIndex(s) - 1 + fsp = len(s) - index - 1 } if fsp == len(s) { @@ -414,16 +414,15 @@ func GetFsp(s string) (fsp int) { return } -// find the last '.' for get fracStr, index = -1 means fracStr not found. -// for format like '2019.01.01 00:00:00', the fracStr should be empty -func getFracIndex(s string) (index int) { +// 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 } } @@ -623,7 +622,7 @@ 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) { - index := getFracIndex(format) + index := GetFracIndex(format) if index > 0 { fracStr = format[index+1:] format = format[:index] diff --git a/types/time_test.go b/types/time_test.go index af43375da3402..a41b71f894486 100644 --- a/types/time_test.go +++ b/types/time_test.go @@ -1308,3 +1308,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) + } +}