LoginSignup
7
0

More than 1 year has passed since last update.

gorm.Model の時刻型の timezone が JST にならない

Last updated at Posted at 2021-11-16

はじめに

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 の仕組みをサラッと解説します。

go
type Model struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
}

gorm.Model とは、gorm で定義されている構造体のことです。
この構造体を埋め込むことにより、簡単にコードを使い回すことができます。

CreatedAtUpdatedAt は、フィールドの値がゼロ値であれば、レコードの更新時または作成時に現在時刻が自動で設定されます。

使い方は下記。

go
type User struct {
  Name     string
  Email    string
  Password string
  gorm.Model
}

シンプルですね。

timezone を変更する

gorm.Modelの CreatedAtUpdatedAt の型を見れば、gorm の時刻には time パッケージが使われていることが分かります。
そうなると、timezone を JST にするためには、time パッケージの方式に従って timezone を変更してあげれば良さそうです。

timezone を設定している、 time.Localを見ていきましょう。

go
// 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を設定します。

.zshrc
export TZ="Asia/Tokyo"
zsh
source ~/.zshrc

これで timezone が JST になるはずです。

先ほどのサンプルコードを再度実行してみます。

go
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)
}
zsh
$ go run main.go
#=> 2021-11-16 19:58:24.658214 +0900 UTC

環境変数を設定したのに、何故か timezone が JST に変わっていません...。

テーブルのカラムが原因

調べたところ、created_atupdated_atdeleted_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) に変えてしまいましょう。

sql
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 を実行してみます。

zsh
$ go run main.go
#=> 2021-11-16 19:58:24.658214 +0900 JST

timezone が UTC から JST になりました!:hugging:

おわりに

go(gorm) ではなく、PostgreSQL の知識不足が原因だった気がします。
PostgreSQL は本格的にキャッチアップした経験がないので、良い機会だと思って触ってみます。
バランス良くキャッチアップすることが大事ですね...!

参考

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0