From 4d4678a4dd5ea86303b73e9560f5c19d46c1a51b Mon Sep 17 00:00:00 2001 From: Rustin Date: Fri, 20 Dec 2019 22:38:25 +0800 Subject: [PATCH] types: Fix potential timezone related bugs caused by `gotime.Local` (#13833) --- server/util_test.go | 11 +++++++++-- types/convert_test.go | 7 +++---- types/time.go | 2 +- types/time_test.go | 41 +++++++++++++++++++++++----------------- util/codec/codec_test.go | 3 ++- 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/server/util_test.go b/server/util_test.go index 5d0ed4784b..490470a478 100644 --- a/server/util_test.go +++ b/server/util_test.go @@ -26,6 +26,7 @@ import ( "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/types/json" "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/util/mock" "github.com/pingcap/tidb/util/testleak" ) @@ -163,7 +164,13 @@ func (s *testUtilSuite) TestDumpTextValue(c *C) { var d types.Datum - time, err := types.ParseTime(nil, "2017-01-05 23:59:59.575601", mysql.TypeDatetime, 0) + sc := mock.NewContext().GetSessionVars().StmtCtx + sc.IgnoreZeroInDate = true + losAngelesTz, err := time.LoadLocation("America/Los_Angeles") + c.Assert(err, IsNil) + sc.TimeZone = losAngelesTz + + time, err := types.ParseTime(sc, "2017-01-05 23:59:59.575601", mysql.TypeDatetime, 0) c.Assert(err, IsNil) d.SetMysqlTime(time) columns[0].Type = mysql.TypeDatetime @@ -171,7 +178,7 @@ func (s *testUtilSuite) TestDumpTextValue(c *C) { c.Assert(err, IsNil) c.Assert(mustDecodeStr(c, bs), Equals, "2017-01-06 00:00:00") - duration, err := types.ParseDuration(nil, "11:30:45", 0) + duration, err := types.ParseDuration(sc, "11:30:45", 0) c.Assert(err, IsNil) d.SetMysqlDuration(duration) columns[0].Type = mysql.TypeDuration diff --git a/types/convert_test.go b/types/convert_test.go index 8d482ba27d..97a76e9346 100644 --- a/types/convert_test.go +++ b/types/convert_test.go @@ -155,15 +155,15 @@ func (s *testTypeConvertSuite) TestConvertType(c *C) { vv, err := Convert(v, ft) c.Assert(err, IsNil) c.Assert(vv.(Duration).String(), Equals, "10:11:12.1") - - vd, err := ParseTime(nil, "2010-10-10 10:11:11.12345", mysql.TypeDatetime, 2) + sc := &stmtctx.StatementContext{TimeZone: time.UTC} + vd, err := ParseTime(sc, "2010-10-10 10:11:11.12345", mysql.TypeDatetime, 2) c.Assert(vd.String(), Equals, "2010-10-10 10:11:11.12") c.Assert(err, IsNil) v, err = Convert(vd, ft) c.Assert(err, IsNil) c.Assert(v.(Duration).String(), Equals, "10:11:11.1") - vt, err := ParseTime(&stmtctx.StatementContext{TimeZone: time.UTC}, "2010-10-10 10:11:11.12345", mysql.TypeTimestamp, 2) + vt, err := ParseTime(sc, "2010-10-10 10:11:11.12345", mysql.TypeTimestamp, 2) c.Assert(vt.String(), Equals, "2010-10-10 10:11:11.12") c.Assert(err, IsNil) v, err = Convert(vt, ft) @@ -249,7 +249,6 @@ func (s *testTypeConvertSuite) TestConvertType(c *C) { // Test Datum.ToDecimal with bad number. d := NewDatum("hello") - sc := new(stmtctx.StatementContext) _, err = d.ToDecimal(sc) c.Assert(terror.ErrorEqual(err, ErrBadNumber), IsTrue, Commentf("err %v", err)) diff --git a/types/time.go b/types/time.go index 21110c2245..1d5b06cfa4 100644 --- a/types/time.go +++ b/types/time.go @@ -824,7 +824,7 @@ func parseDatetime(sc *stmtctx.StatementContext, str string, fsp int8, isFloat b tmp := FromDate(year, month, day, hour, minute, second, microsecond) if overflow { // Convert to Go time and add 1 second, to handle input like 2017-01-05 08:40:59.575601 - t1, err := tmp.GoTime(gotime.Local) + t1, err := tmp.GoTime(sc.TimeZone) if err != nil { return ZeroDatetime, errors.Trace(err) } diff --git a/types/time_test.go b/types/time_test.go index a3979c19d4..a3dbfa1d58 100644 --- a/types/time_test.go +++ b/types/time_test.go @@ -491,7 +491,7 @@ func (s *testTimeSuite) TestCodec(c *C) { } for _, test := range tbl { - t, err := types.ParseTime(nil, test, mysql.TypeDatetime, types.MaxFsp) + t, err := types.ParseTime(sc, test, mysql.TypeDatetime, types.MaxFsp) c.Assert(err, IsNil) packed, _ = t.ToPackedUint() @@ -582,6 +582,9 @@ func (s *testTimeSuite) TestParseTimeFromNum(c *C) { func (s *testTimeSuite) TestToNumber(c *C) { sc := mock.NewContext().GetSessionVars().StmtCtx sc.IgnoreZeroInDate = true + losAngelesTz, err := time.LoadLocation("America/Los_Angeles") + c.Assert(err, IsNil) + sc.TimeZone = losAngelesTz defer testleak.AfterTest(c)() tblDateTime := []struct { Input string @@ -600,7 +603,7 @@ func (s *testTimeSuite) TestToNumber(c *C) { } for _, test := range tblDateTime { - t, err := types.ParseTime(nil, test.Input, mysql.TypeDatetime, test.Fsp) + t, err := types.ParseTime(sc, test.Input, mysql.TypeDatetime, test.Fsp) c.Assert(err, IsNil) c.Assert(t.ToNumber().String(), Equals, test.Expect) } @@ -623,7 +626,7 @@ func (s *testTimeSuite) TestToNumber(c *C) { } for _, test := range tblDate { - t, err := types.ParseTime(nil, test.Input, mysql.TypeDate, 0) + t, err := types.ParseTime(sc, test.Input, mysql.TypeDate, 0) c.Assert(err, IsNil) c.Assert(t.ToNumber().String(), Equals, test.Expect) } @@ -721,7 +724,8 @@ func (s *testTimeSuite) TestRoundFrac(c *C) { c.Assert(nv.String(), Equals, t.Except) } // test different time zone - losAngelesTz, _ := time.LoadLocation("America/Los_Angeles") + losAngelesTz, err := time.LoadLocation("America/Los_Angeles") + c.Assert(err, IsNil) sc.TimeZone = losAngelesTz tbl = []struct { Input string @@ -783,6 +787,10 @@ func (s *testTimeSuite) TestRoundFrac(c *C) { } func (s *testTimeSuite) TestConvert(c *C) { + sc := mock.NewContext().GetSessionVars().StmtCtx + sc.IgnoreZeroInDate = true + losAngelesTz, _ := time.LoadLocation("America/Los_Angeles") + sc.TimeZone = losAngelesTz defer testleak.AfterTest(c)() tbl := []struct { Input string @@ -799,7 +807,7 @@ func (s *testTimeSuite) TestConvert(c *C) { } for _, t := range tbl { - v, err := types.ParseTime(nil, t.Input, mysql.TypeDatetime, t.Fsp) + v, err := types.ParseTime(sc, t.Input, mysql.TypeDatetime, t.Fsp) c.Assert(err, IsNil) nv, err := v.ConvertToDuration() c.Assert(err, IsNil) @@ -815,23 +823,22 @@ func (s *testTimeSuite) TestConvert(c *C) { {"11:30:45.123456", 0}, {"1 11:30:45.999999", 0}, } - - sc := mock.NewContext().GetSessionVars().StmtCtx + // test different time zone. sc.TimeZone = time.UTC for _, t := range tblDuration { v, err := types.ParseDuration(sc, t.Input, t.Fsp) c.Assert(err, IsNil) - year, month, day := time.Now().In(time.UTC).Date() - n := time.Date(year, month, day, 0, 0, 0, 0, time.UTC) + year, month, day := time.Now().In(sc.TimeZone).Date() + n := time.Date(year, month, day, 0, 0, 0, 0, sc.TimeZone) t, err := v.ConvertToTime(sc, mysql.TypeDatetime) c.Assert(err, IsNil) - // TODO: Consider time_zone variable. - t1, _ := t.Time.GoTime(time.UTC) + t1, _ := t.Time.GoTime(sc.TimeZone) c.Assert(t1.Sub(n), Equals, v.Duration) } } func (s *testTimeSuite) TestCompare(c *C) { + sc := &stmtctx.StatementContext{TimeZone: time.UTC} defer testleak.AfterTest(c)() tbl := []struct { Arg1 string @@ -846,7 +853,7 @@ func (s *testTimeSuite) TestCompare(c *C) { } for _, t := range tbl { - v1, err := types.ParseTime(nil, t.Arg1, mysql.TypeDatetime, types.MaxFsp) + v1, err := types.ParseTime(sc, t.Arg1, mysql.TypeDatetime, types.MaxFsp) c.Assert(err, IsNil) ret, err := v1.CompareString(nil, t.Arg2) @@ -854,7 +861,7 @@ func (s *testTimeSuite) TestCompare(c *C) { c.Assert(ret, Equals, t.Ret) } - v1, err := types.ParseTime(nil, "2011-10-10 11:11:11", mysql.TypeDatetime, types.MaxFsp) + v1, err := types.ParseTime(sc, "2011-10-10 11:11:11", mysql.TypeDatetime, types.MaxFsp) c.Assert(err, IsNil) res, err := v1.CompareString(nil, "Test should error") c.Assert(err, NotNil) @@ -1015,11 +1022,11 @@ func (s *testTimeSuite) TestTimeAdd(c *C) { TimeZone: time.UTC, } for _, t := range tbl { - v1, err := types.ParseTime(nil, t.Arg1, mysql.TypeDatetime, types.MaxFsp) + v1, err := types.ParseTime(sc, t.Arg1, mysql.TypeDatetime, types.MaxFsp) c.Assert(err, IsNil) dur, err := types.ParseDuration(sc, t.Arg2, types.MaxFsp) c.Assert(err, IsNil) - result, err := types.ParseTime(nil, t.Ret, mysql.TypeDatetime, types.MaxFsp) + result, err := types.ParseTime(sc, t.Ret, mysql.TypeDatetime, types.MaxFsp) c.Assert(err, IsNil) v2, err := v1.Add(sc, dur) c.Assert(err, IsNil) @@ -1652,9 +1659,9 @@ func (s *testTimeSuite) TestTimeSub(c *C) { TimeZone: time.UTC, } for _, t := range tbl { - v1, err := types.ParseTime(nil, t.Arg1, mysql.TypeDatetime, types.MaxFsp) + v1, err := types.ParseTime(sc, t.Arg1, mysql.TypeDatetime, types.MaxFsp) c.Assert(err, IsNil) - v2, err := types.ParseTime(nil, t.Arg2, mysql.TypeDatetime, types.MaxFsp) + v2, err := types.ParseTime(sc, t.Arg2, mysql.TypeDatetime, types.MaxFsp) c.Assert(err, IsNil) dur, err := types.ParseDuration(sc, t.Ret, types.MaxFsp) c.Assert(err, IsNil) diff --git a/util/codec/codec_test.go b/util/codec/codec_test.go index 5855f1389a..42df1fcea3 100644 --- a/util/codec/codec_test.go +++ b/util/codec/codec_test.go @@ -530,7 +530,8 @@ func (s *testCodecSuite) TestBytes(c *C) { } func parseTime(c *C, s string) types.Time { - m, err := types.ParseTime(nil, s, mysql.TypeDatetime, types.DefaultFsp) + sc := &stmtctx.StatementContext{TimeZone: time.UTC} + m, err := types.ParseTime(sc, s, mysql.TypeDatetime, types.DefaultFsp) c.Assert(err, IsNil) return m }