diff --git a/db.go b/db.go index ccab03e..ba899de 100644 --- a/db.go +++ b/db.go @@ -47,13 +47,13 @@ func OpenTestConnection() (db *gorm.DB, err error) { case "mysql": log.Println("testing mysql...") if dbDSN == "" { - dbDSN = "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local" + dbDSN = "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=UTC" } db, err = gorm.Open(mysql.Open(dbDSN), &gorm.Config{}) case "postgres": log.Println("testing postgres...") if dbDSN == "" { - dbDSN = "user=gorm password=gorm host=localhost dbname=gorm port=9920 sslmode=disable TimeZone=Asia/Shanghai" + dbDSN = "user=gorm password=gorm host=localhost dbname=gorm port=9920 sslmode=disable TimeZone=UTC" } db, err = gorm.Open(postgres.Open(dbDSN), &gorm.Config{}) case "sqlserver": diff --git a/docker-compose.yml b/docker-compose.yml index 9ab4ddb..c7f830f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,7 +15,7 @@ services: ports: - 9920:5432 environment: - - TZ=Asia/Shanghai + - TZ=UTC - POSTGRES_DB=gorm - POSTGRES_USER=gorm - POSTGRES_PASSWORD=gorm diff --git a/go.mod b/go.mod index f194634..483e539 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,15 @@ module gorm.io/playground -go 1.22.0 +go 1.23.0 toolchain go1.23.3 require ( + github.com/jackc/pgx/v5 v5.7.2 + github.com/stretchr/testify v1.10.0 gorm.io/driver/mysql v1.5.7 - gorm.io/driver/postgres v1.5.10 - gorm.io/driver/sqlite v1.5.6 + gorm.io/driver/postgres v1.5.11 + gorm.io/driver/sqlite v1.5.7 gorm.io/driver/sqlserver v1.5.4 gorm.io/gen v0.3.26 gorm.io/gorm v1.25.12 @@ -15,25 +17,28 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect - github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-sql-driver/mysql v1.9.0 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect - github.com/jackc/pgx/v5 v5.7.1 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/kr/text v0.2.0 // indirect github.com/mattn/go-sqlite3 v1.14.24 // indirect - github.com/microsoft/go-mssqldb v1.7.2 // indirect - golang.org/x/crypto v0.29.0 // indirect - golang.org/x/mod v0.22.0 // indirect - golang.org/x/sync v0.9.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.20.0 // indirect - golang.org/x/tools v0.27.0 // indirect - gorm.io/datatypes v1.2.4 // indirect + github.com/microsoft/go-mssqldb v1.8.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + golang.org/x/crypto v0.35.0 // indirect + golang.org/x/mod v0.23.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/text v0.22.0 // indirect + golang.org/x/tools v0.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/datatypes v1.2.5 // indirect gorm.io/hints v1.1.2 // indirect gorm.io/plugin/dbresolver v1.5.3 // indirect ) diff --git a/main_test.go b/main_test.go index 60a388f..6fcf0e3 100644 --- a/main_test.go +++ b/main_test.go @@ -2,6 +2,18 @@ package main import ( "testing" + "time" + + "github.com/stretchr/testify/assert" + "gorm.io/driver/postgres" + "gorm.io/gorm" + + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgtype" + "github.com/jackc/pgx/v5/pgxpool" + "github.com/jackc/pgx/v5/stdlib" ) // GORM_REPO: https://github.com/go-gorm/gorm.git @@ -18,3 +30,91 @@ func TestGORM(t *testing.T) { t.Errorf("Failed, got error: %v", err) } } + +func TestTimeZone(t *testing.T) { + birthday := time.Date(2023, 11, 12, 9, 7, 18, 0, time.UTC) + user := User{Name: "TimeZone UTC", Birthday: &birthday} + + if err := DB.Create(&user).Error; err != nil { + t.Errorf("Failed, got error: %v", err) + t.FailNow() + } + + birthdayResult := *user.Birthday + assert.Equal(t, birthday, birthdayResult) + + userResult := User{} + + if err := DB.Where("name = ?", "TimeZone UTC").First(&userResult).Error; err != nil { + t.Errorf("Failed, got error: %v", err) + t.FailNow() + } + + birthdayResult = *userResult.Birthday + assert.Equal(t, birthday, birthdayResult) + +} + +func TestPGXPooltoSqlDB(t *testing.T) { + if DB.Dialector.Name() != "postgres" { + t.Skip("test only runs in postgres") + } + + // The following test shows how to open a pgxpool with the forced return of timezone as UTC. + ctx := context.Background() + + uri := "user=gorm password=gorm host=localhost dbname=gorm port=9920 sslmode=disable TimeZone=UTC" + + config, err := pgxpool.ParseConfig(uri) + if err != nil { + t.Errorf("Failed, got error: %v", err) + t.FailNow() + } + + config.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error { + conn.TypeMap().RegisterType(&pgtype.Type{ + Name: "timestamptz", + OID: pgtype.TimestamptzOID, + Codec: &pgtype.TimestamptzCodec{ScanLocation: time.UTC}, // It would be better to parse the connection string for TimeZone and use that here. Or have some other way of specifying it. + }) + + return nil + } + + pool, err := pgxpool.NewWithConfig(ctx, config) + if err != nil { + t.Errorf("Failed, got error: %v", err) + t.FailNow() + } + + // test connection to ensure all is well + if err = pool.Ping(ctx); err != nil { + pool.Close() + t.Errorf("Failed, got error: %v", err) + t.FailNow() + } + + nativeDB := stdlib.OpenDBFromPool(pool) + db, err := gorm.Open(postgres.New(postgres.Config{Conn: nativeDB})) + if err != nil { + t.Errorf("Failed, got error: %v", err) + t.FailNow() + } + + birthday := time.Date(2023, 11, 12, 9, 7, 18, 0, time.UTC) + userResult := User{} + + if err := db.Where("name = ?", "TimeZone UTC").First(&userResult).Error; err != nil { + t.Errorf("Failed, got error: %v", err) + t.FailNow() + } + + birthdayResult := *userResult.Birthday + assert.Equal(t, birthday, birthdayResult) + + // Need some way to allow the closing of the connection pool after the gorm connection has closed. + if sqlDB, _ := db.DB(); sqlDB != nil { + sqlDB.Close() + pool.Close() + } +}