{
    "componentChunkName": "component---src-templates-post-js",
    "path": "/gormtopostgresqlnotimezone/",
    "result": {"data":{"ghostPost":{"id":"Ghost__Post__610e0e8e3986b000013a5338","title":"GORMとPostgreSQLのtimezone?","slug":"gormtopostgresqlnotimezone","featured":false,"feature_image":"https://ghost.tech.anti-pattern.co.jp/content/images/2021/08/29791364_1246754985456610_5906763196705800192_n.jpg","excerpt":"こんにちは、Anti-Pattern Inc.の塚本です。\n\n弊社ではGolang使用してアプリケーション開発をしています。DBはPostgreSQLでORマッパーにGORMを利用しています。\n\nGolangコンテナ、DBコンテナはタイムゾーンを指定していないのでデフォルト値が適用されています。これをJSTにしたかった！というブログです。\n\n■Golangコンテナ\n$ docker exec -it 58d644eaec04 bash\nroot@58d644eaec04:/app# date  \n**Sat May  8 10:43:18 UTC 2021**\n\n■DBコンテナ\nroot@2e3c2a3ad755:/# psql -U ***** -d *****\npsql (11.9 (Debian 11.9-1.pgdg90+1))  \nType \"help\" for help.  \n=> show timezone;\nTimeZone  \n------------  \n**UTC**\n\n\ntime.Now()を出力した結果です。UTCが設定されています\n\nnow := time.","custom_excerpt":null,"visibility":"public","created_at_pretty":"07 August, 2021","published_at_pretty":"14 May, 2021","updated_at_pretty":"07 August, 2021","created_at":"2021-08-07T13:39:42.000+09:00","published_at":"2021-05-14T13:55:00.000+09:00","updated_at":"2021-08-07T13:56:27.000+09:00","meta_title":null,"meta_description":null,"og_description":null,"og_image":null,"og_title":null,"twitter_description":null,"twitter_image":null,"twitter_title":null,"authors":[{"name":"takeshi tsukamoto","slug":"zhong","bio":null,"profile_image":null,"twitter":null,"facebook":null,"website":null}],"primary_author":{"name":"takeshi tsukamoto","slug":"zhong","bio":null,"profile_image":null,"twitter":null,"facebook":null,"website":null},"primary_tag":null,"tags":[],"plaintext":"こんにちは、Anti-Pattern Inc.の塚本です。\n\n弊社ではGolang使用してアプリケーション開発をしています。DBはPostgreSQLでORマッパーにGORMを利用しています。\n\nGolangコンテナ、DBコンテナはタイムゾーンを指定していないのでデフォルト値が適用されています。これをJSTにしたかった！というブログです。\n\n■Golangコンテナ\n$ docker exec -it 58d644eaec04 bash\nroot@58d644eaec04:/app# date  \n**Sat May  8 10:43:18 UTC 2021**\n\n■DBコンテナ\nroot@2e3c2a3ad755:/# psql -U ***** -d *****\npsql (11.9 (Debian 11.9-1.pgdg90+1))  \nType \"help\" for help.  \n=> show timezone;\nTimeZone  \n------------  \n**UTC**\n\n\ntime.Now()を出力した結果です。UTCが設定されています\n\nnow := time.Now()  \npretty.Println(now)\n// 一部省略  \ntime.Time{\n    wall: 0xc01dba5be36c39a0,\n    ext:  9264217901,\n    loc:  &time.Location{\n        name: \"Local\",\n        zone: {\n            {name:\"UTC\", offset:0, isDST:false},\n        },\n        extend:     \"UTC0\",\n    },\n}\n\n\ntime packageのzoneinfo.goを見ると、Locationが設定される優先順位が記述されています。\n\n// Local represents the system's local time zone.  \n// On Unix systems, Local consults the **TZ environment**  \n// variable to find the time zone to use. No TZ means  \n// use the system default **/etc/localtime**.  \n// TZ=\"\" means use UTC.  \n// TZ=\"foo\" means use file foo in the system timezone directory.var Local *Location = &localLoc\n\n\nコンテナにJSTのtimezoneを設定します\n\nRUN apt-get update && apt-get install -y tzdata  \nRUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime  \nENV TZ=Asia/Tokyo\n\n\nイメージをリビルドして再起動した結果です\nOSのtimezoneがJSTに変更されてます\n\n$ docker exec -it 05be8001430e bash\nroot@05be8001430e:/app# date  \n**Sat May  8 20:34:47 JST 2021**\nroot@05be8001430e:/app# strings /etc/localtime  \nTZif2  \nTZif2  \nJST-9\n\n\ntime.Now()を出力した結果です。 name: “Asia/Tokyo”に変更され、JSTで出力されます\n\nnow := time.Now()  \npretty.Println(now)\n// 一部省略  \n\ntime.Time{\n    wall: 0xc01dbbf41503be10,\n    ext:  320877338601,\n    loc:  &time.Location{\n        name: \"Asia/Tokyo\",\n        zone: {\n            {name:\"LMT\", offset:33539, isDST:false},\n            {name:\"JDT\", offset:36000, isDST:true},\n            {name:\"JST\", offset:32400, isDST:false},\n            {name:\"JST\", offset:32400, isDST:false},\n        },\n        extend:     \"JST-9\",\n    },\n}\n\npretty.Println(now.String())  \n2021-05-08 20:48:48.8286243 +0900 **JST** m=+2.744851101\n\n\n今回は直接変更して検証しています\n\nALTER DATABASE \"****\" SET timezone TO 'Asia/Tokyo';\n$ docker exec -it f27946c4ba27 bash  \nroot@f27946c4ba27:/# psql -U *** -d *****\npsql (11.9 (Debian 11.9-1.pgdg90+1))  \nType \"help\" for help.\n=> show timezone;  \nTimeZone  \n------------  \nAsia/Tokyo\n\n\nデータ登録時のログで確認してますが、現在日時（JST）で設定されています。ここまでは想定通りです。\n\nINSERT INTO \"test_names\" (\"created_at\",\"updated_at\",\"deleted_at\",\"name\") VALUES ('2021-05-08 21:08:45.757','2021-05-08 21:08:45.757',NULL,'test') RETURNING \"id\"\n\n\n次にデータ取得\n\nDBから取得したデータのCreatedAtをログに出力します。\nhttps://pkg.go.dev/gorm.io/gorm#Model\n\npretty.Println(result.Model)\npretty.Println(result.Model.CreatedAt.String())\npretty.Println(result.Model.CreatedAt.Location())\ngorm.Model{\n    ID:        0x5,\n    CreatedAt: time.Time{\n        wall: 0x2d28f578,\n        ext:  63756104925,\n        loc:  (*time.Location)(nil),\n    },\n    UpdatedAt: time.Time{\n        wall: 0x2d28f578,\n        ext:  63756104925,\n        loc:  (*time.Location)(nil),\n    },\n    DeletedAt: gorm.DeletedAt{},\n}\n2021-05-08 21:08:45.757659 +0000 UTC\n&time.Location{\n    name:       \"UTC\",\n    zone:       nil,\n    tx:         nil,\n    extend:     \"\",\n    cacheStart: 0,\n    cacheEnd:   0,\n    cacheZone:  (*time.zone)(nil),\n}\n\n\nあれ？UTCになってる。locがnil ムムム・・\n\nちなみにDBのカラム定義はtimezoneを持ってません\n\ncreated\\_at   | timestamp without time zone \n\n\nでは、timezoneを持たせて確認\n\nALTER TABLE tableName ALTER COLUMN created\\_at TYPE timestamp with time zone\n\n\nlocに設定されて、timezoneはJSTと判定されてます（これが期待していた結果です）\n\n// 抜粋  \ngorm.Model{\n    ID:        0x5,\n    CreatedAt: time.Time{\n        wall: 0x2d28f578,\n        ext:  63756072525,\n        loc:  &time.Location{\n            name: \"Asia/Tokyo\",\n            zone: {\n                {name:\"LMT\", offset:33539, isDST:false},\n                {name:\"JDT\", offset:36000, isDST:true},\n                {name:\"JST\", offset:32400, isDST:false},\n                {name:\"JST\", offset:32400, isDST:false},\n            },\n           extend:     \"JST-9\",\n            cacheStart: 9223372036854775807,\n            cacheEnd:   9223372036854775807,\n            cacheZone:  &time.zone{(CYCLIC REFERENCE)},\n        },\n    },\n    UpdatedAt: time.Time{\n        wall: 0x2d28f578,\n        ext:  63756104925,\n        loc:  (*time.Location)(nil),\n    },\n    DeletedAt: gorm.DeletedAt{},\n}\n2021-05-08 21:08:45.757659 +0900 JST\n&time.Location{\n    name: \"Asia/Tokyo\",\n    zone: {\n        {name:\"LMT\", offset:33539, isDST:false},\n        {name:\"JDT\", offset:36000, isDST:true},\n        {name:\"JST\", offset:32400, isDST:false},\n        {name:\"JST\", offset:32400, isDST:false},\n    },\n   extend:     \"JST-9\",\n    cacheStart: 9223372036854775807,\n    cacheEnd:   9223372036854775807,\n    cacheZone:  &time.zone{(CYCLIC REFERENCE)},\n}\n\n\nDBとGolangの実行環境のtimezoneを変えて取得したデータのtimezoneを確認した結果はこちら\n\n\nDBのカラムがtimezoneを保持していないと、Golang(GORM)で変数にバインドしたtime型のlocationはUTCとなる（①）。timezoneを保持している場合は、Golang(GORM)実行環境のtimezoneが設定される。\n\n個人的には、DBとGolang実行環境でシステムは分離されているので、DBの設定を優先させても良いかと思いました（③）。\n\nどうするかは、これから検討しようと思います。","html":"<!--kg-card-begin: markdown--><p>こんにちは、Anti-Pattern Inc.の塚本です。</p>\n<p>弊社ではGolang使用してアプリケーション開発をしています。DBはPostgreSQLでORマッパーにGORMを利用しています。</p>\n<p>Golangコンテナ、DBコンテナはタイムゾーンを指定していないのでデフォルト値が適用されています。これをJSTにしたかった！というブログです。</p>\n<pre><code>■Golangコンテナ\n$ docker exec -it 58d644eaec04 bash\nroot@58d644eaec04:/app# date  \n**Sat May  8 10:43:18 UTC 2021**\n\n■DBコンテナ\nroot@2e3c2a3ad755:/# psql -U ***** -d *****\npsql (11.9 (Debian 11.9-1.pgdg90+1))  \nType &quot;help&quot; for help.  \n=&gt; show timezone;\nTimeZone  \n------------  \n**UTC**\n</code></pre>\n<p>time.Now()を出力した結果です。UTCが設定されています</p>\n<pre><code>now := time.Now()  \npretty.Println(now)\n// 一部省略  \ntime.Time{\n    wall: 0xc01dba5be36c39a0,\n    ext:  9264217901,\n    loc:  &amp;time.Location{\n        name: &quot;Local&quot;,\n        zone: {\n            {name:&quot;UTC&quot;, offset:0, isDST:false},\n        },\n        extend:     &quot;UTC0&quot;,\n    },\n}\n</code></pre>\n<p>time packageのzoneinfo.goを見ると、Locationが設定される優先順位が記述されています。</p>\n<pre><code>// Local represents the system's local time zone.  \n// On Unix systems, Local consults the **TZ environment**  \n// variable to find the time zone to use. No TZ means  \n// use the system default **/etc/localtime**.  \n// TZ=&quot;&quot; means use UTC.  \n// TZ=&quot;foo&quot; means use file foo in the system timezone directory.var Local *Location = &amp;localLoc\n</code></pre>\n<p>コンテナにJSTのtimezoneを設定します</p>\n<pre><code>RUN apt-get update &amp;&amp; apt-get install -y tzdata  \nRUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime  \nENV TZ=Asia/Tokyo\n</code></pre>\n<p>イメージをリビルドして再起動した結果です<br>\nOSのtimezoneがJSTに変更されてます</p>\n<pre><code>$ docker exec -it 05be8001430e bash\nroot@05be8001430e:/app# date  \n**Sat May  8 20:34:47 JST 2021**\nroot@05be8001430e:/app# strings /etc/localtime  \nTZif2  \nTZif2  \nJST-9\n</code></pre>\n<p>time.Now()を出力した結果です。 name: “Asia/Tokyo”に変更され、JSTで出力されます</p>\n<pre><code>now := time.Now()  \npretty.Println(now)\n// 一部省略  \n\ntime.Time{\n    wall: 0xc01dbbf41503be10,\n    ext:  320877338601,\n    loc:  &amp;time.Location{\n        name: &quot;Asia/Tokyo&quot;,\n        zone: {\n            {name:&quot;LMT&quot;, offset:33539, isDST:false},\n            {name:&quot;JDT&quot;, offset:36000, isDST:true},\n            {name:&quot;JST&quot;, offset:32400, isDST:false},\n            {name:&quot;JST&quot;, offset:32400, isDST:false},\n        },\n        extend:     &quot;JST-9&quot;,\n    },\n}\n\npretty.Println(now.String())  \n2021-05-08 20:48:48.8286243 +0900 **JST** m=+2.744851101\n</code></pre>\n<p>今回は直接変更して検証しています</p>\n<pre><code>ALTER DATABASE &quot;****&quot; SET timezone TO 'Asia/Tokyo';\n$ docker exec -it f27946c4ba27 bash  \nroot@f27946c4ba27:/# psql -U *** -d *****\npsql (11.9 (Debian 11.9-1.pgdg90+1))  \nType &quot;help&quot; for help.\n=&gt; show timezone;  \nTimeZone  \n------------  \nAsia/Tokyo\n</code></pre>\n<p>データ登録時のログで確認してますが、現在日時（JST）で設定されています。ここまでは想定通りです。</p>\n<pre><code>INSERT INTO &quot;test_names&quot; (&quot;created_at&quot;,&quot;updated_at&quot;,&quot;deleted_at&quot;,&quot;name&quot;) VALUES ('2021-05-08 21:08:45.757','2021-05-08 21:08:45.757',NULL,'test') RETURNING &quot;id&quot;\n</code></pre>\n<p>次にデータ取得</p>\n<p>DBから取得したデータのCreatedAtをログに出力します。<br>\n<a href=\"https://pkg.go.dev/gorm.io/gorm#Model\">https://pkg.go.dev/gorm.io/gorm#Model</a></p>\n<pre><code>pretty.Println(result.Model)\npretty.Println(result.Model.CreatedAt.String())\npretty.Println(result.Model.CreatedAt.Location())\ngorm.Model{\n    ID:        0x5,\n    CreatedAt: time.Time{\n        wall: 0x2d28f578,\n        ext:  63756104925,\n        loc:  (*time.Location)(nil),\n    },\n    UpdatedAt: time.Time{\n        wall: 0x2d28f578,\n        ext:  63756104925,\n        loc:  (*time.Location)(nil),\n    },\n    DeletedAt: gorm.DeletedAt{},\n}\n2021-05-08 21:08:45.757659 +0000 UTC\n&amp;time.Location{\n    name:       &quot;UTC&quot;,\n    zone:       nil,\n    tx:         nil,\n    extend:     &quot;&quot;,\n    cacheStart: 0,\n    cacheEnd:   0,\n    cacheZone:  (*time.zone)(nil),\n}\n</code></pre>\n<p>あれ？UTCになってる。locがnil ムムム・・</p>\n<p>ちなみにDBのカラム定義はtimezoneを持ってません</p>\n<pre><code>created\\_at   | timestamp without time zone \n</code></pre>\n<p>では、timezoneを持たせて確認</p>\n<pre><code>ALTER TABLE tableName ALTER COLUMN created\\_at TYPE timestamp with time zone\n</code></pre>\n<p>locに設定されて、timezoneはJSTと判定されてます（これが期待していた結果です）</p>\n<pre><code>// 抜粋  \ngorm.Model{\n    ID:        0x5,\n    CreatedAt: time.Time{\n        wall: 0x2d28f578,\n        ext:  63756072525,\n        loc:  &amp;time.Location{\n            name: &quot;Asia/Tokyo&quot;,\n            zone: {\n                {name:&quot;LMT&quot;, offset:33539, isDST:false},\n                {name:&quot;JDT&quot;, offset:36000, isDST:true},\n                {name:&quot;JST&quot;, offset:32400, isDST:false},\n                {name:&quot;JST&quot;, offset:32400, isDST:false},\n            },\n           extend:     &quot;JST-9&quot;,\n            cacheStart: 9223372036854775807,\n            cacheEnd:   9223372036854775807,\n            cacheZone:  &amp;time.zone{(CYCLIC REFERENCE)},\n        },\n    },\n    UpdatedAt: time.Time{\n        wall: 0x2d28f578,\n        ext:  63756104925,\n        loc:  (*time.Location)(nil),\n    },\n    DeletedAt: gorm.DeletedAt{},\n}\n2021-05-08 21:08:45.757659 +0900 JST\n&amp;time.Location{\n    name: &quot;Asia/Tokyo&quot;,\n    zone: {\n        {name:&quot;LMT&quot;, offset:33539, isDST:false},\n        {name:&quot;JDT&quot;, offset:36000, isDST:true},\n        {name:&quot;JST&quot;, offset:32400, isDST:false},\n        {name:&quot;JST&quot;, offset:32400, isDST:false},\n    },\n   extend:     &quot;JST-9&quot;,\n    cacheStart: 9223372036854775807,\n    cacheEnd:   9223372036854775807,\n    cacheZone:  &amp;time.zone{(CYCLIC REFERENCE)},\n}\n</code></pre>\n<p>DBとGolangの実行環境のtimezoneを変えて取得したデータのtimezoneを確認した結果はこちら<br>\n<img src=\"https://ghost.tech.anti-pattern.co.jp/content/images/2021/08/1_K4rZ9j7TfC_EZmtgYnniKg.png\" alt=\"1_K4rZ9j7TfC_EZmtgYnniKg\" loading=\"lazy\"></p>\n<p>DBのカラムがtimezoneを保持していないと、Golang(GORM)で変数にバインドしたtime型のlocationはUTCとなる（①）。timezoneを保持している場合は、Golang(GORM)実行環境のtimezoneが設定される。</p>\n<p>個人的には、DBとGolang実行環境でシステムは分離されているので、DBの設定を優先させても良いかと思いました（③）。</p>\n<p>どうするかは、これから検討しようと思います。</p>\n<!--kg-card-end: markdown-->","url":"https://ghost.tech.anti-pattern.co.jp/gormtopostgresqlnotimezone/","canonical_url":null,"uuid":"eb241e3f-368f-4eef-8d0d-60ed5b381128","page":null,"codeinjection_foot":null,"codeinjection_head":null,"codeinjection_styles":null,"comment_id":"610e0e8e3986b000013a5338","reading_time":3}},"pageContext":{"slug":"gormtopostgresqlnotimezone"}},
    "staticQueryHashes": ["176528973","2358152166","2561578252","2731221146","4145280475"]}