はじめに
Go で、文字列から日時へパースする際にタイムゾーンの指定で困ったので調べました。
結論から先に述べると、time.Parse
はデフォルトで UTC を設定されます。
基本的にはロケーションが設定できるtime.ParseInLocation
を利用するのが妥当だろうという見解に至りました。
注意:本記事に記載しているコードはエラー処理を無視しています。実際に利用する場合はちゃんとエラー処理しましょう。
time.Parse
はデフォルトで UTC になる
パースする文字列が2018-01-01 00:00:00
のように、タイムインジケータを指定していない場合は UTC として解釈されます。
日本時間にするには、2018-01-01 00:00:00 (JST)
のようにタイムインジケータを指定する必要があります。
// タイムインジケータがないのでUTCと解釈される
// => 2018-01-01 00:00:00 +0000 UTC
t, _ := time.Parse("2006-01-02 15:04:05", "2018-01-01 00:00:00")
タイムゾーンを指定する方法
方法は下記の 2 種類あります。
1. time.Parse
でタイムインジケータを含めてパースする
パース時のフォーマットにタイムインジケータを含ませることで指定できます。
// タイムインジケータを指定すると、そのタイムゾーンで解釈される
// タイムインジケータを認識させたい場合はformatに含める
// => 2018-01-01 00:00:00 +0900 JST
t2, _ := time.Parse("2006-01-02 15:04:05 (MST)", "2018-01-01 00:00:00 (JST)")
2. time.ParseInLocation
でロケーションを指定する
ロケーションを指定してパースするtime.ParseInLocation
を使用することで、2018-01-01 00:00:00
のようなタイムインジケータのない文字列もパースできるようになります。
// 第3引数でtime.Locationを指定することで、タイムインジケータなしでタイムゾーンを指定できる
// => 2018-01-01 00:00:00 +0900 JST
jst, _ := time.LoadLocation("Asia/Tokyo")
t1, _ := time.ParseInLocation("2006-01-02 15:04:05", "2018-01-01 00:00:00", jst)
// 第3引数でロケーションを指定し、かつ文字列内でタイムインジケータが指定されている場合は、
// タイムインジケータの設定が有効になる
// => 2018-01-01 00:00:00 +0000 UTC
t2, _ := time.ParseInLocation("2006-01-02 15:04:05 (MST)", "2018-01-01 00:00:00 (UTC)", jst)
time.Parse
とtime.ParseInLocation
について
2 つのメソッドの仕様をまとめます。
time.Parse
- 文字列にタイムインジケータが指定されていない場合は、UTC になる
- 文字列でタイムインジケータが指定されている場合は、指定したタイムゾーンになる
time.ParseInLocation
- 文字列でタイムインジケータが指定されていない場合は、第 3 引数で指定されたタイムゾーンになる
- 文字列でタイムインジケータが指定されている場合は、指定したタイムゾーンになる
付録
挙動チェック用コード
package main
import (
"fmt"
"time"
)
func main() {
jst, _ := time.LoadLocation("Asia/Tokyo")
a, _ := time.Parse("2006-01-02 15:04:05", "2018-01-01 00:00:00")
b, _ := time.Parse("2006-01-02 15:04:05 (MST)", "2018-01-01 00:00:00 (JST)")
c, _ := time.ParseInLocation("2006-01-02 15:04:05", "2018-01-01 00:00:00", jst)
d, _ := time.ParseInLocation("2006-01-02 15:04:05 (MST)", "2018-01-01 00:00:00 (UTC)", jst)
fmt.Println("a: ", a)
fmt.Println("b: ", b)
fmt.Println("c: ", c)
fmt.Println("d: ", d)
}
実行結果
a: 2018-01-01 00:00:00 +0000 UTC
b: 2018-01-01 00:00:00 +0900 JST
c: 2018-01-01 00:00:00 +0900 JST
d: 2018-01-01 00:00:00 +0000 UTC
おわりに
time.Parse
でも期待する処理は実装できますが、パースしたい文字列にタイムインジケータがついている場合はおそらくそれほど多くありません。time.ParseInLocation
を使用しておくほうがより問題は起きづらいでしょう。