diff --git a/expression/builtin_time.go b/expression/builtin_time.go index 343496f53ae44..8dba9aff6a7e6 100644 --- a/expression/builtin_time.go +++ b/expression/builtin_time.go @@ -5127,21 +5127,22 @@ func (b *builtinConvertTzSig) Clone() builtinFunc { } // evalTime evals CONVERT_TZ(dt,from_tz,to_tz). +// `CONVERT_TZ` function returns NULL if the arguments are invalid. // See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_convert-tz func (b *builtinConvertTzSig) evalTime(row chunk.Row) (types.Time, bool, error) { dt, isNull, err := b.args[0].EvalTime(b.ctx, row) if isNull || err != nil { - return types.Time{}, true, err + return types.Time{}, true, nil } fromTzStr, isNull, err := b.args[1].EvalString(b.ctx, row) - if isNull || err != nil { - return types.Time{}, true, err + if isNull || err != nil || fromTzStr == "" { + return types.Time{}, true, nil } toTzStr, isNull, err := b.args[2].EvalString(b.ctx, row) - if isNull || err != nil { - return types.Time{}, true, err + if isNull || err != nil || toTzStr == "" { + return types.Time{}, true, nil } fromTzMatched := b.timezoneRegex.MatchString(fromTzStr) @@ -5150,17 +5151,17 @@ func (b *builtinConvertTzSig) evalTime(row chunk.Row) (types.Time, bool, error) if !fromTzMatched && !toTzMatched { fromTz, err := time.LoadLocation(fromTzStr) if err != nil { - return types.Time{}, true, err + return types.Time{}, true, nil } toTz, err := time.LoadLocation(toTzStr) if err != nil { - return types.Time{}, true, err + return types.Time{}, true, nil } t, err := dt.Time.GoTime(fromTz) if err != nil { - return types.Time{}, true, err + return types.Time{}, true, nil } return types.Time{ @@ -5172,7 +5173,7 @@ func (b *builtinConvertTzSig) evalTime(row chunk.Row) (types.Time, bool, error) if fromTzMatched && toTzMatched { t, err := dt.Time.GoTime(time.Local) if err != nil { - return types.Time{}, true, err + return types.Time{}, true, nil } return types.Time{ diff --git a/expression/builtin_time_test.go b/expression/builtin_time_test.go index d3bff3f275dfe..eebb80b524d51 100644 --- a/expression/builtin_time_test.go +++ b/expression/builtin_time_test.go @@ -2493,8 +2493,8 @@ func (s *testEvaluatorSuite) TestSecToTime(c *C) { func (s *testEvaluatorSuite) TestConvertTz(c *C) { tests := []struct { t interface{} - fromTz string - toTz string + fromTz interface{} + toTz interface{} Success bool expect string }{ @@ -2506,11 +2506,20 @@ func (s *testEvaluatorSuite) TestConvertTz(c *C) { {"2004-01-01 12:00:00", "-00:00", "+13:00", true, "2004-01-02 01:00:00"}, {"2004-01-01 12:00:00", "-00:00", "-13:00", true, ""}, {"2004-01-01 12:00:00", "-00:00", "-12:88", true, ""}, - {"2004-01-01 12:00:00", "+10:82", "GMT", false, ""}, + {"2004-01-01 12:00:00", "+10:82", "GMT", true, ""}, {"2004-01-01 12:00:00", "+00:00", "GMT", true, ""}, {"2004-01-01 12:00:00", "GMT", "+00:00", true, ""}, {20040101, "+00:00", "+10:32", true, "2004-01-01 10:32:00"}, {3.14159, "+00:00", "+10:32", true, ""}, + {"2004-01-01 12:00:00", "", "GMT", true, ""}, + {"2004-01-01 12:00:00", "GMT", "", true, ""}, + {"2004-01-01 12:00:00", "a", "GMT", true, ""}, + {"2004-01-01 12:00:00", "0", "GMT", true, ""}, + {"2004-01-01 12:00:00", "GMT", "a", true, ""}, + {"2004-01-01 12:00:00", "GMT", "0", true, ""}, + {nil, "GMT", "+00:00", true, ""}, + {"2004-01-01 12:00:00", nil, "+00:00", true, ""}, + {"2004-01-01 12:00:00", "GMT", nil, true, ""}, } fc := funcs[ast.ConvertTz] for _, test := range tests { @@ -2518,8 +2527,8 @@ func (s *testEvaluatorSuite) TestConvertTz(c *C) { s.datumsToConstants( []types.Datum{ types.NewDatum(test.t), - types.NewStringDatum(test.fromTz), - types.NewStringDatum(test.toTz)})) + types.NewDatum(test.fromTz), + types.NewDatum(test.toTz)})) c.Assert(err, IsNil) d, err := evalBuiltinFunc(f, chunk.Row{}) if test.Success { diff --git a/expression/integration_test.go b/expression/integration_test.go index 6ec20ba22af4e..4a158cc2e1b24 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -1823,9 +1823,16 @@ func (s *testIntegrationSuite) TestTimeBuiltin(c *C) { // for convert_tz result = tk.MustQuery(`select convert_tz("2004-01-01 12:00:00", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00.01", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00.01234567", "+00:00", "+10:32");`) result.Check(testkit.Rows("2004-01-01 22:32:00 2004-01-01 22:32:00.01 2004-01-01 22:32:00.012346")) - // TODO: release the following test after fix #4462 - //result = tk.MustQuery(`select convert_tz(20040101, "+00:00", "+10:32"), convert_tz(20040101.01, "+00:00", "+10:32"), convert_tz(20040101.01234567, "+00:00", "+10:32");`) - //result.Check(testkit.Rows("2004-01-01 10:32:00 2004-01-01 10:32:00.00 2004-01-01 10:32:00.000000")) + result = tk.MustQuery(`select convert_tz(20040101, "+00:00", "+10:32"), convert_tz(20040101.01, "+00:00", "+10:32"), convert_tz(20040101.01234567, "+00:00", "+10:32");`) + result.Check(testkit.Rows("2004-01-01 10:32:00 2004-01-01 10:32:00.00 2004-01-01 10:32:00.000000")) + result = tk.MustQuery(`select convert_tz(NULL, "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", NULL, "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", NULL);`) + result.Check(testkit.Rows(" ")) + result = tk.MustQuery(`select convert_tz("a", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", "a", "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", "a");`) + result.Check(testkit.Rows(" ")) + result = tk.MustQuery(`select convert_tz("", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", "", "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", "");`) + result.Check(testkit.Rows(" ")) + result = tk.MustQuery(`select convert_tz("0", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", "0", "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", "0");`) + result.Check(testkit.Rows(" ")) // for from_unixtime tk.MustExec(`set @@session.time_zone = "+08:00"`)