はじめに
gorm.Model の timezone が UTC から JST にならず、小一時間頭を悩ませたため、その過程で得たものをアウトプットします。
Go(gorm) + PostgreSQL を使っている方が対象です。
環境
- Go 1.17.1
- PostgreSQL 13.4
- Gorm v2
結論
PostgreSQL の時刻カラムの型が、timestamp(without time zone) だったことが原因でした。
(結論だけ知りたい方向けです)
解決の流れ
gorm.Model の仕組み
まずは gorm.Model の仕組みをサラッと解説します。
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
gorm.Model とは、gorm で定義されている構造体のことです。
この構造体を埋め込むことにより、簡単にコードを使い回すことができます。
CreatedAt
、UpdatedAt
は、フィールドの値がゼロ値であれば、レコードの更新時または作成時に現在時刻が自動で設定されます。
使い方は下記。
type User struct {
Name string
Email string
Password string
gorm.Model
}
シンプルですね。
timezone を変更する
gorm.Modelの CreatedAt
、UpdatedAt
の型を見れば、gorm の時刻には time パッケージが使われていることが分かります。
そうなると、timezone を JST にするためには、time パッケージの方式に従って timezone を変更してあげれば良さそうです。
timezone を設定している、 time.Local
を見ていきましょう。
// Local represents the system's local time zone.
// On Unix systems, Local consults the TZ environment
// variable to find the time zone to use. No TZ means
// use the system default /etc/localtime.
// TZ="" means use UTC.
// TZ="foo" means use file foo in the system timezone directory.
var Local *Location = &localLoc
上記のコメント通り、環境変数 TZ
を設定します。
export TZ="Asia/Tokyo"
source ~/.zshrc
これで timezone が JST になるはずです。
先ほどのサンプルコードを再度実行してみます。
package main
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
type User struct {
gorm.Model
}
func main() {
db, _ := gorm.Open(postgres.Open("url"))
u := new(User)
db.Create(u)
fmt.Println(u.CreatedAt)
}
$ go run main.go
#=> 2021-11-16 19:58:24.658214 +0900 UTC
環境変数を設定したのに、何故か timezone が JST に変わっていません...。
テーブルのカラムが原因
調べたところ、created_at
、updated_at
、deleted_at
カラムの型が原因でした。
PostgreSQL の日付 + 時刻型には、timezone が付いている型とそうでない型の2種類があります。
- timestamp (without time zone)
- timestamp (with time zone)
公式ドキュメントによると、timezone の有無を指定しないでカラムを作成した場合は timestamp (without time zone) 型になってしまうそうです。
注意: 標準SQLでは、単なるtimestampという記述はtimestamp without time zoneと同じであることを要求します。 PostgreSQLはこれに準じます。 timestamp with time zoneはtimestamptzと省略することが許容されています。これはPostgreSQL独自の拡張です。
僕は timezone の有無を指定しないでカラムを作成してしまったので、今回の状況に当てはまります。
DB に接続して ALTER TABLE
を実行し、カラムの型を timestamp (with time zone) に変えてしまいましょう。
ALTER TABLE テーブル名 ALTER COLUMN created_at TYPE timestamp with time zone;
ALTER TABLE テーブル名 ALTER COLUMN updated_at TYPE timestamp with time zone;
ALTER TABLE テーブル名 ALTER COLUMN deleted_at TYPE timestamp with time zone;
これで再度 go を実行してみます。
$ go run main.go
#=> 2021-11-16 19:58:24.658214 +0900 JST
timezone が UTC から JST になりました!
おわりに
go(gorm) ではなく、PostgreSQL の知識不足が原因だった気がします。
PostgreSQL は本格的にキャッチアップした経験がないので、良い機会だと思って触ってみます。
バランス良くキャッチアップすることが大事ですね...!