2018/07/31

Goでtime.Parseを使うときのタイムゾーンについて

はじめに

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.Parsetime.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を使用しておくほうがより問題は起きづらいでしょう。