diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 2c1a9191f1..05f143507f 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -2005,9 +2005,6 @@ func (b *builtinStrToDateDurationSig) evalDuration(row chunk.Row) (types.Duratio if !succ { return types.Duration{}, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String())) } - if b.ctx.GetSessionVars().SQLMode.HasNoZeroDateMode() && (t.Year() == 0 || t.Month() == 0 || t.Day() == 0) { - return types.Duration{}, true, handleInvalidTimeError(b.ctx, types.ErrWrongValue.GenWithStackByArgs(types.DateTimeStr, t.String())) - } t.SetFsp(int8(b.tp.Decimal)) dur, err := t.ConvertToDuration() return dur, err != nil, err diff --git a/expression/integration_test.go b/expression/integration_test.go index c087156faf..fae9ecdf93 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -1938,13 +1938,28 @@ func (s *testIntegrationSuite2) TestTimeBuiltin(c *C) { result.Check(testkit.Rows("2017-01-01 2017-01-01 12:20:59 12:20:59")) result = tk.MustQuery("select str_to_date('aaa01-01-2017', 'aaa%d-%m-%Y'), str_to_date('59:20:12 aaa01-01-2017', '%s:%i:%H aaa%d-%m-%Y'), str_to_date('59:20:12aaa', '%s:%i:%Haaa')") result.Check(testkit.Rows("2017-01-01 2017-01-01 12:20:59 12:20:59")) + result = tk.MustQuery("select str_to_date('01-01-2017', '%d'), str_to_date('59', '%d-%Y')") // TODO: MySQL returns " ". result.Check(testkit.Rows("0000-00-01 ")) - tk.MustQuery("show warnings").Check(testutil.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00'")) + result = tk.MustQuery("show warnings") + result.Sort().Check(testutil.RowsWithSep("|", + "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00'", + "Warning|1292|Truncated incorrect datetime value: '01-01-2017'")) + result = tk.MustQuery("select str_to_date('2018-6-1', '%Y-%m-%d'), str_to_date('2018-6-1', '%Y-%c-%d'), str_to_date('59:20:1', '%s:%i:%k'), str_to_date('59:20:1', '%s:%i:%l')") result.Check(testkit.Rows("2018-06-01 2018-06-01 01:20:59 01:20:59")) + result = tk.MustQuery("select str_to_date('2020-07-04 11:22:33 PM c', '%Y-%m-%d %r')") + result.Check(testkit.Rows("2020-07-04 23:22:33")) + result = tk.MustQuery("show warnings") + result.Check(testutil.RowsWithSep("|", "Warning|1292|Truncated incorrect datetime value: '2020-07-04 11:22:33 PM c'")) + + result = tk.MustQuery("select str_to_date('11:22:33 PM', ' %r')") + result.Check(testkit.Rows("23:22:33")) + result = tk.MustQuery("show warnings") + result.Check(testkit.Rows()) + // for maketime tk.MustExec(`drop table if exists t`) tk.MustExec(`create table t(a double, b float, c decimal(10,4));`) diff --git a/types/time.go b/types/time.go index d97cc55958..0b8312386d 100644 --- a/types/time.go +++ b/types/time.go @@ -2678,7 +2678,8 @@ func abbrDayOfMonth(day int) string { func (t *Time) StrToDate(sc *stmtctx.StatementContext, date, format string) bool { ctx := make(map[string]int) var tm CoreTime - if !strToDate(&tm, date, format, ctx) { + success, warning := strToDate(&tm, date, format, ctx) + if !success { t.SetCoreTime(ZeroCoreTime) t.SetType(mysql.TypeDatetime) t.SetFsp(0) @@ -2690,7 +2691,15 @@ func (t *Time) StrToDate(sc *stmtctx.StatementContext, date, format string) bool t.SetCoreTime(tm) t.SetType(mysql.TypeDatetime) - return t.check(sc) == nil + if t.check(sc) != nil { + return false + } + if warning { + // Only append this warning when success but still need warning. + // Currently this only happens when `date` has extra characters at the end. + sc.AppendWarning(ErrTruncatedWrongVal.GenWithStackByArgs(DateTimeStr, date)) + } + return true } // mysqlTimeFix fixes the Time use the values in the context. @@ -2728,30 +2737,35 @@ func mysqlTimeFix(t *CoreTime, ctx map[string]int) error { return nil } -// strToDate converts date string according to format, returns true on success, +// strToDate converts date string according to format, // the value will be stored in argument t or ctx. -func strToDate(t *CoreTime, date string, format string, ctx map[string]int) bool { +// The second return value is true when success but still need to append a warning. +func strToDate(t *CoreTime, date string, format string, ctx map[string]int) (success bool, warning bool) { date = skipWhiteSpace(date) format = skipWhiteSpace(format) token, formatRemain, succ := getFormatToken(format) if !succ { - return false + return false, false } if token == "" { - // Extra characters at the end of date are ignored. - return true + if len(date) != 0 { + // Extra characters at the end of date are ignored, but a warning should be reported at this case. + return true, true + } + // Normal case. Both token and date are empty now. + return true, false } if len(date) == 0 { ctx[token] = 0 - return true + return true, false } dateRemain, succ := matchDateWithToken(t, date, token, ctx) if !succ { - return false + return false, false } return strToDate(t, dateRemain, formatRemain, ctx) @@ -2948,8 +2962,10 @@ func time12Hour(t *CoreTime, input string, ctx map[string]int) (string, bool) { switch { case strings.HasPrefix(remain, "AM"): t.setHour(uint8(hour)) + remain = strings.TrimPrefix(remain, "AM") case strings.HasPrefix(remain, "PM"): t.setHour(uint8(hour + 12)) + remain = strings.TrimPrefix(remain, "PM") default: return input, false }