From 8e9dfd9aaac5da116ee30830994399bf9a4e6a72 Mon Sep 17 00:00:00 2001 From: James Palawaga Date: Wed, 20 Oct 2021 14:43:18 -0400 Subject: [PATCH] BREAK: prefer UTC over Local tz for deserialization Inspired by https://github.com/alexbrainman/odbc/issues/157 --- column.go | 12 ++++++++---- driver.go | 1 + foxpro_test.go | 8 ++++---- mssql_test.go | 24 ++++++++++++------------ mysql_test.go | 2 +- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/column.go b/column.go index f103c71..2787220 100644 --- a/column.go +++ b/column.go @@ -131,6 +131,10 @@ func (c *BaseColumn) Value(buf []byte) (driver.Value, error) { if len(buf) > 0 { p = unsafe.Pointer(&buf[0]) } + loc := time.UTC + if drv.Loc != nil { + loc = drv.Loc + } switch c.CType { case api.SQL_C_BIT: return buf[0] != 0, nil @@ -152,7 +156,7 @@ func (c *BaseColumn) Value(buf []byte) (driver.Value, error) { t := (*api.SQL_TIMESTAMP_STRUCT)(p) r := time.Date(int(t.Year), time.Month(t.Month), int(t.Day), int(t.Hour), int(t.Minute), int(t.Second), int(t.Fraction), - time.Local) + loc) return r, nil case api.SQL_C_GUID: t := (*api.SQLGUID)(p) @@ -169,19 +173,19 @@ func (c *BaseColumn) Value(buf []byte) (driver.Value, error) { case api.SQL_C_DATE: t := (*api.SQL_DATE_STRUCT)(p) r := time.Date(int(t.Year), time.Month(t.Month), int(t.Day), - 0, 0, 0, 0, time.Local) + 0, 0, 0, 0, loc) return r, nil case api.SQL_C_TIME: t := (*api.SQL_TIME_STRUCT)(p) r := time.Date(1, time.January, 1, - int(t.Hour), int(t.Minute), int(t.Second), 0, time.Local) + int(t.Hour), int(t.Minute), int(t.Second), 0, loc) return r, nil case api.SQL_C_BINARY: if c.SQLType == api.SQL_SS_TIME2 { t := (*api.SQL_SS_TIME2_STRUCT)(p) r := time.Date(1, time.January, 1, int(t.Hour), int(t.Minute), int(t.Second), int(t.Fraction), - time.Local) + loc) return r, nil } return buf, nil diff --git a/driver.go b/driver.go index 12e4fa3..41f4cd5 100644 --- a/driver.go +++ b/driver.go @@ -18,6 +18,7 @@ type Driver struct { Stats h api.SQLHENV // environment handle initErr error + Loc *time.Location } func initDriver() error { diff --git a/foxpro_test.go b/foxpro_test.go index 8502a1b..fd318ca 100644 --- a/foxpro_test.go +++ b/foxpro_test.go @@ -50,7 +50,7 @@ func TestFoxPro(t *testing.T) { num_2_0: sql.NullFloat64{Float64: 1, Valid: true}, num_20_0: 1232543, num_6_3: 12.73, - date: time.Date(2012, 5, 19, 0, 0, 0, 0, time.Local), + date: time.Date(2012, 5, 19, 0, 0, 0, 0, time.UTC), float_2_0: 23, float_20_0: 12345678901234560, float_6_3: 12.345, @@ -62,7 +62,7 @@ func TestFoxPro(t *testing.T) { num_2_0: sql.NullFloat64{Float64: 23, Valid: true}, num_20_0: 4564568, num_6_3: 2, - date: time.Date(2012, 5, 20, 0, 0, 0, 0, time.Local), + date: time.Date(2012, 5, 20, 0, 0, 0, 0, time.UTC), float_2_0: 1, float_20_0: 234, float_6_3: 0.123, @@ -74,7 +74,7 @@ func TestFoxPro(t *testing.T) { num_2_0: sql.NullFloat64{Float64: 4, Valid: true}, num_20_0: 1234567890123456000, num_6_3: 99.99, - date: time.Date(2012, 5, 21, 0, 0, 0, 0, time.Local), + date: time.Date(2012, 5, 21, 0, 0, 0, 0, time.UTC), float_2_0: 23, float_20_0: 457768, float_6_3: 99, @@ -86,7 +86,7 @@ func TestFoxPro(t *testing.T) { num_2_0: sql.NullFloat64{Float64: 0, Valid: false}, num_20_0: 234456, num_6_3: 0.123, - date: time.Date(2012, 5, 22, 0, 0, 0, 0, time.Local), + date: time.Date(2012, 5, 22, 0, 0, 0, 0, time.UTC), float_2_0: 65, float_20_0: 234, float_6_3: 1, diff --git a/mssql_test.go b/mssql_test.go index b1d79b6..28b2ba8 100644 --- a/mssql_test.go +++ b/mssql_test.go @@ -286,7 +286,7 @@ func TestMSSQLCreateInsertDelete(t *testing.T) { age: 5, isGirl: true, weight: 15.5, - dob: time.Date(2000, 5, 10, 11, 1, 1, 0, time.Local), + dob: time.Date(2000, 5, 10, 11, 1, 1, 0, time.UTC), data: []byte{0x0, 0x0, 0xb, 0xad, 0xc0, 0xde}, canBeNull: sql.NullString{"aa", true}, }, @@ -294,7 +294,7 @@ func TestMSSQLCreateInsertDelete(t *testing.T) { age: 3, isGirl: false, weight: 26.12, - dob: time.Date(2009, 5, 10, 11, 1, 1, 123e6, time.Local), + dob: time.Date(2009, 5, 10, 11, 1, 1, 123e6, time.UTC), data: []byte{0x0}, canBeNull: sql.NullString{"bbb", true}, }, @@ -315,11 +315,11 @@ func TestMSSQLCreateInsertDelete(t *testing.T) { t.Fatal(err) } } - _, err = s.Exec("chris", 25, 0, 50, time.Date(2015, 12, 25, 0, 0, 0, 0, time.Local), "ccc", nil) + _, err = s.Exec("chris", 25, 0, 50, time.Date(2015, 12, 25, 0, 0, 0, 0, time.UTC), "ccc", nil) if err != nil { t.Fatal(err) } - _, err = s.Exec("null", 0, 0, 0, time.Date(2015, 12, 25, 1, 2, 3, 0, time.Local), nil, nil) + _, err = s.Exec("null", 0, 0, 0, time.Date(2015, 12, 25, 1, 2, 3, 0, time.UTC), nil, nil) if err != nil { t.Fatal(err) } @@ -621,10 +621,10 @@ var typeTests = []typeTest{ {"select cast(NULL as nvarchar(5))", match(nil)}, // datetime, smalldatetime - {"select cast('20151225' as datetime)", match(time.Date(2015, 12, 25, 0, 0, 0, 0, time.Local))}, - {"select cast('2007-05-08 12:35:29.123' as datetime)", match(time.Date(2007, 5, 8, 12, 35, 29, 123e6, time.Local))}, + {"select cast('20151225' as datetime)", match(time.Date(2015, 12, 25, 0, 0, 0, 0, time.UTC))}, + {"select cast('2007-05-08 12:35:29.123' as datetime)", match(time.Date(2007, 5, 8, 12, 35, 29, 123e6, time.UTC))}, {"select cast(NULL as datetime)", match(nil)}, - {"select cast('2007-05-08 12:35:29.123' as smalldatetime)", match(time.Date(2007, 5, 8, 12, 35, 0, 0, time.Local))}, + {"select cast('2007-05-08 12:35:29.123' as smalldatetime)", match(time.Date(2007, 5, 8, 12, 35, 0, 0, time.UTC))}, // uniqueidentifier {"select cast('0e984725-c51c-4bf4-9960-e1c80e27aba0' as uniqueidentifier)", match("0e984725-c51c-4bf4-9960-e1c80e27aba0")}, @@ -675,12 +675,12 @@ var typeMSSpecificTests = []typeTest{ var typeMSSQL2008Tests = []typeTest{ // datetime2 - {"select cast('20151225' as datetime2)", match(time.Date(2015, 12, 25, 0, 0, 0, 0, time.Local))}, - {"select cast('2007-05-08 12:35:29.1234567' as datetime2)", match(time.Date(2007, 5, 8, 12, 35, 29, 1234567e2, time.Local))}, + {"select cast('20151225' as datetime2)", match(time.Date(2015, 12, 25, 0, 0, 0, 0, time.UTC))}, + {"select cast('2007-05-08 12:35:29.1234567' as datetime2)", match(time.Date(2007, 5, 8, 12, 35, 29, 1234567e2, time.UTC))}, {"select cast(NULL as datetime2)", match(nil)}, // time(7) - {"select cast('12:35:29.1234567' as time(7))", match(time.Date(1, 1, 1, 12, 35, 29, 1234567e2, time.Local))}, + {"select cast('12:35:29.1234567' as time(7))", match(time.Date(1, 1, 1, 12, 35, 29, 1234567e2, time.UTC))}, {"select cast(NULL as time(7))", match(nil)}, } @@ -1079,7 +1079,7 @@ func TestMSSQLDatetime2Param(t *testing.T) { db.Exec("drop table dbo.temp") exec(t, db, "create table dbo.temp (dt datetime2)") - expect := time.Date(2007, 5, 8, 12, 35, 29, 1234567e2, time.Local) + expect := time.Date(2007, 5, 8, 12, 35, 29, 1234567e2, time.UTC) _, err = db.Exec("insert into dbo.temp (dt) values (?)", expect) if err != nil { t.Fatal(err) @@ -1257,7 +1257,7 @@ var paramTypeTests = []struct { {"4001 large unicode string value", "ntext", strings.Repeat("\u0421", 4001)}, {"very large string value", "text", strings.Repeat("a", 10000)}, // datetime - {"datetime overflow", "datetime", time.Date(2013, 9, 9, 14, 07, 15, 123e6, time.Local)}, + {"datetime overflow", "datetime", time.Date(2013, 9, 9, 14, 07, 15, 123e6, time.UTC)}, // binary blobs {"small blob", "varbinary", make([]byte, 1)}, {"very large blob", "varbinary(max)", make([]byte, 100000)}, diff --git a/mysql_test.go b/mysql_test.go index 5d44f26..0cc58cb 100644 --- a/mysql_test.go +++ b/mysql_test.go @@ -45,7 +45,7 @@ func TestMYSQLTime(t *testing.T) { exec(t, db, "create table temp(id int not null auto_increment primary key, time time)") now := time.Now() // SQL_TIME_STRUCT only supports hours, minutes and seconds - now = time.Date(1, time.January, 1, now.Hour(), now.Minute(), now.Second(), 0, time.Local) + now = time.Date(1, time.January, 1, now.Hour(), now.Minute(), now.Second(), 0, time.UTC) _, err = db.Exec("insert into temp (time) values(?)", now) if err != nil { t.Fatal(err)