diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index 06d4ad1663a09..8b54104915779 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -1251,6 +1251,11 @@ func (s *testEvaluatorSuite) TestStrToDate(c *C) { {"2016 11 22 16 50 22", `%Y%m%d%H%i%s`, true, time.Date(2016, 11, 22, 16, 50, 22, 0, time.Local)}, {"16-50-22 2016 11 22", `%H-%i-%s%Y%m%d`, true, time.Date(2016, 11, 22, 16, 50, 22, 0, time.Local)}, {"16-50 2016 11 22", `%H-%i-%s%Y%m%d`, false, time.Time{}}, + {"15-01-2001 1:59:58.999", "%d-%m-%Y %I:%i:%s.%f", true, time.Date(2001, 1, 15, 1, 59, 58, 999000000, time.Local)}, + {"15-01-2001 1:59:58.1", "%d-%m-%Y %H:%i:%s.%f", true, time.Date(2001, 1, 15, 1, 59, 58, 100000000, time.Local)}, + {"15-01-2001 1:59:58.", "%d-%m-%Y %H:%i:%s.%f", true, time.Date(2001, 1, 15, 1, 59, 58, 000000000, time.Local)}, + {"15-01-2001 1:9:8.999", "%d-%m-%Y %H:%i:%s.%f", true, time.Date(2001, 1, 15, 1, 9, 8, 999000000, time.Local)}, + {"15-01-2001 1:9:8.999", "%d-%m-%Y %H:%i:%S.%f", true, time.Date(2001, 1, 15, 1, 9, 8, 999000000, time.Local)}, } fc := funcs[ast.StrToDate] diff --git a/types/time.go b/types/time.go index 5010297404052..4ad9d9d45cc94 100644 --- a/types/time.go +++ b/types/time.go @@ -2152,8 +2152,8 @@ var dateFormatParserTable = map[string]dateFormatParser{ "%e": dayOfMonthNumeric, // Day of the month, numeric (0..31) "%f": microSeconds, // Microseconds (000000..999999) "%h": hour24TwoDigits, // Hour (01..12) - "%H": hour24TwoDigits, // Hour (01..12) - "%I": hour24TwoDigits, // Hour (01..12) + "%H": hour24Numeric, // Hour (00..23) + "%I": hour12Numeric, // Hour (01..12) "%i": minutesNumeric, // Minutes, numeric (00..59) "%j": dayOfYearThreeDigits, // Day of year (001..366) "%k": hour24Numeric, // Hour (0..23) @@ -2257,21 +2257,27 @@ func hour24TwoDigits(t *MysqlTime, input string, ctx map[string]int) (string, bo } func secondsNumeric(t *MysqlTime, input string, ctx map[string]int) (string, bool) { - v, succ := parseDigits(input, 2) + result := oneOrTwoDigitRegex.FindString(input) + length := len(result) + + v, succ := parseDigits(input, length) if !succ || v >= 60 { return input, false } t.second = uint8(v) - return input[2:], true + return input[length:], true } func minutesNumeric(t *MysqlTime, input string, ctx map[string]int) (string, bool) { - v, succ := parseDigits(input, 2) + result := oneOrTwoDigitRegex.FindString(input) + length := len(result) + + v, succ := parseDigits(input, length) if !succ || v >= 60 { return input, false } t.minute = uint8(v) - return input[2:], true + return input[length:], true } const time12HourLen = len("hh:mm:ssAM") @@ -2368,6 +2374,9 @@ var oneOrTwoDigitRegex = regexp.MustCompile("^[0-9]{1,2}") // twoDigitRegex: it was just for two digit number string. Ex: "01" or "12" var twoDigitRegex = regexp.MustCompile("^[1-9][0-9]?") +// oneToSixDigitRegex: it was just for [0, 999999] +var oneToSixDigitRegex = regexp.MustCompile("^[0-9]{0,6}") + // parseTwoNumeric is used for pattens 0..31 0..24 0..60 and so on. // It returns the parsed int, and remain data after parse. func parseTwoNumeric(input string) (int, string) { @@ -2427,15 +2436,23 @@ func hour12Numeric(t *MysqlTime, input string, ctx map[string]int) (string, bool } func microSeconds(t *MysqlTime, input string, ctx map[string]int) (string, bool) { - if len(input) < 6 { - return input, false + result := oneToSixDigitRegex.FindString(input) + length := len(result) + if length == 0 { + t.microsecond = 0 + return input, true } - v, err := strconv.ParseUint(input[:6], 10, 64) - if err != nil { + + v, ok := parseDigits(input, length) + + if !ok { return input, false } + for v > 0 && v*10 < 1000000 { + v *= 10 + } t.microsecond = uint32(v) - return input[6:], true + return input[length:], true } func yearNumericFourDigits(t *MysqlTime, input string, ctx map[string]int) (string, bool) {