diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go index 51e0b08bee170..78f2140e2b43b 100644 --- a/sessionctx/variable/session.go +++ b/sessionctx/variable/session.go @@ -16,6 +16,7 @@ package variable import ( "strings" "sync" + "time" "github.com/juju/errors" "github.com/pingcap/tidb/mysql" @@ -144,6 +145,10 @@ type SessionVars struct { // CurrInsertValues is used to record current ValuesExpr's values. // See http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_values CurrInsertValues interface{} + + // Per-connection time zones. Each client that connects has its own time zone setting, given by the session time_zone variable. + // See https://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html + TimeZone *time.Location } // NewSessionVars creates a session vars object. @@ -224,6 +229,7 @@ const ( SQLModeVar = "sql_mode" AutocommitVar = "autocommit" CharacterSetResults = "character_set_results" + TimeZone = "time_zone" ) // GetTiDBSystemVar gets variable value for name. diff --git a/sessionctx/varsutil/varsutil.go b/sessionctx/varsutil/varsutil.go index 91a408f53d8fa..1a78a6536c16a 100644 --- a/sessionctx/varsutil/varsutil.go +++ b/sessionctx/varsutil/varsutil.go @@ -85,6 +85,8 @@ func SetSessionSystemVar(vars *variable.SessionVars, name string, value types.Da return errors.Trace(err) } switch name { + case variable.TimeZone: + vars.TimeZone = parseTimeZone(sVal) case variable.SQLModeVar: sVal = strings.ToUpper(sVal) if strings.Contains(sVal, "STRICT_TRANS_TABLES") || strings.Contains(sVal, "STRICT_ALL_TABLES") { @@ -112,6 +114,28 @@ func SetSessionSystemVar(vars *variable.SessionVars, name string, value types.Da return nil } +func parseTimeZone(s string) *time.Location { + if s == "SYSTEM" { + // TODO: Support global time_zone variable, it should be set to global time_zone value. + return time.Local + } + + loc, err := time.LoadLocation(s) + if err == nil { + return loc + } + + // The value can be given as a string indicating an offset from UTC, such as '+10:00' or '-6:00'. + if strings.HasPrefix(s, "+") || strings.HasPrefix(s, "-") { + d, err := types.ParseDuration(s[1:], 0) + if err == nil { + return time.FixedZone("UTC", int(d.Duration/time.Second)) + } + } + + return nil +} + func setSnapshotTS(s *variable.SessionVars, sVal string) error { if sVal == "" { s.SnapshotTS = 0 diff --git a/sessionctx/varsutil/varsutil_test.go b/sessionctx/varsutil/varsutil_test.go index 6d35ecee20a25..ffd19abf618eb 100644 --- a/sessionctx/varsutil/varsutil_test.go +++ b/sessionctx/varsutil/varsutil_test.go @@ -15,6 +15,7 @@ package varsutil import ( "testing" + "time" . "github.com/pingcap/check" "github.com/pingcap/tidb/sessionctx/variable" @@ -57,7 +58,7 @@ func (s *testVarsutilSuite) TestVarsutil(c *C) { c.Assert(SetSessionSystemVar(v, "character_set_results", types.Datum{}), IsNil) - // Test case for get TiDBSkipConstraintCheck session variable + // Test case for get TiDBSkipConstraintCheck session variable. val, err = GetSessionSystemVar(v, variable.TiDBSkipConstraintCheck) c.Assert(err, IsNil) c.Assert(val, Equals, "0") @@ -77,7 +78,7 @@ func (s *testVarsutilSuite) TestVarsutil(c *C) { c.Assert(err, IsNil) c.Assert(val, Equals, "1") - // Test case for get TiDBSkipDDLWait session variable + // Test case for get TiDBSkipDDLWait session variable. val, err = GetSessionSystemVar(v, variable.TiDBSkipDDLWait) c.Assert(val, Equals, "0") c.Assert(v.SkipDDLWait, IsFalse) @@ -87,4 +88,19 @@ func (s *testVarsutilSuite) TestVarsutil(c *C) { c.Assert(v.SkipDDLWait, IsTrue) val, err = GetSessionSystemVar(v, variable.TiDBSkipDDLWait) c.Assert(val, Equals, "1") + + // Test case for time_zone session variable. + SetSessionSystemVar(v, variable.TimeZone, types.NewStringDatum("Europe/Helsinki")) + c.Assert(v.TimeZone.String(), Equals, "Europe/Helsinki") + SetSessionSystemVar(v, variable.TimeZone, types.NewStringDatum("US/Eastern")) + c.Assert(v.TimeZone.String(), Equals, "US/Eastern") + SetSessionSystemVar(v, variable.TimeZone, types.NewStringDatum("SYSTEM")) + c.Assert(v.TimeZone.String(), Equals, "Local") + SetSessionSystemVar(v, variable.TimeZone, types.NewStringDatum("+10:00")) + c.Assert(v.TimeZone.String(), Equals, "UTC") + t1 := time.Date(2000, 1, 1, 0, 0, 0, 0, v.TimeZone) + t2 := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) + c.Assert(t2.Sub(t1), Equals, 10*time.Hour) + SetSessionSystemVar(v, variable.TimeZone, types.NewStringDatum("-6:00")) + c.Assert(v.TimeZone.String(), Equals, "UTC") }