Golang で日付操作(UTC→JST)
UTC → JSTに変えたくなった。
やったこと
package main import ( "fmt" "time" ) const ( ISO8601Format = "2006-01-02T15:04:05Z" ) func main() { // UTC value := "2021-08-10T13:41:32Z" jst, _ := time.LoadLocation("Asia/Tokyo") r, _ := time.ParseInLocation(ISO8601Format, value, jst) fmt.Println(r) // 2021-08-10 13:41:32 +0900 JST }
locationは変わっているが、+9時間されてない。
本当はこうなってほしい。
2021-08-10 22:41:32 +0900 JST
こう変えた
func main() { value := "2021-08-10T13:41:32Z" jst, _ := time.LoadLocation("Asia/Tokyo") r, _ := time.Parse(ISO8601Format, value) r2 := r.In(jst) fmt.Println(r) // 2021-08-10 13:41:32 +0000 UTC fmt.Println(r2) // 2021-08-10 22:41:32 +0900 JST }
意図通りにはなった。
が time.In は何をしているのか...
time.In は何をしているのか
// In returns a copy of t representing the same time instant, but // with the copy's location information set to loc for display // purposes. // // In panics if loc is nil. func (t Time) In(loc *Location) Time { if loc == nil { panic("time: missing Location in call to Time.In") } t.setLoc(loc) return t }
ほぉ。
Inは、同じ時間の瞬間を表すtのコピーを返しますが、表示のためにコピーの位置情報をlocに設定します。 locがnilの場合、Inはパニックになります。
Copyした time に位置情報を設定しているらしい。
ただ、中身は time.setLoc をしているだけ。
time.setLoc は何をしているのか
// setLoc sets the location associated with the time. func (t *Time) setLoc(loc *Location) { if loc == &utcLoc { loc = nil } t.stripMono() t.loc = loc }
ロケーションを設定しているだけのよう…
setLocは、時間に関連付けられたロケーションを設定します。
time.stripMono とは…
time.stripMono は何をしているのか
// stripMono strips the monotonic clock reading in t. func (t *Time) stripMono() { if t.wall&hasMonotonic != 0 { t.ext = t.sec() t.wall &= nsecMask } }
stripMonoは、tに読み込まれた単調なクロックをストリップします。
なるほどね。全然わからん。
動かしながらみる
Parseした段階ではこう
r, _ := time.Parse(ISO8601Format, value)
Parseした際のデフォルトTimezoneはUTCなのでこの段階では +0000
setLoc にきた段階ではこう
func (t *Time) setLoc(loc *Location) {
変わらない。
stripMono に来た段階でも
if t.wall&hasMonotonic != 0
この分岐に入らず終わったので変わらなかった
時間が変わったのは setLoc のここ
t.loc = loc
Copy した(?) time に location を追加したところで +0900 された
う〜ん・・・
とりあえず time の loc に jst 入れると変わる。
time.loc
// loc specifies the Location that should be used to // determine the minute, hour, month, day, and year // that correspond to this Time. // The nil location means UTC. // All UTC times are represented with loc==nil, never loc==&utcLoc. loc *Location
locは、このTimeに対応する分、時間、月、日、年を決定するために使用すべきLocationを指定します。nilのLocationはUTCを意味します。全てのUTCタイムはloc==nilで表され、決してloc==&utcLocではありません。
へぇ。location == nil は UTC と…
そもそも time.ParseInLocation で jst 設定したときはなぜ変わらなかった??
jst, _ := time.LoadLocation("Asia/Tokyo") r, _ := time.ParseInLocation(ISO8601Format, value, jst)
time.ParseInLocation は何をしているのか
// ParseInLocation is like Parse but differs in two important ways. // First, in the absence of time zone information, Parse interprets a time as UTC; // ParseInLocation interprets the time as in the given location. // Second, when given a zone offset or abbreviation, Parse tries to match it // against the Local location; ParseInLocation uses the given location. func ParseInLocation(layout, value string, loc *Location) (Time, error) { return parse(layout, value, loc, loc) }
実質 time.parse している
time.parse は何をしているのか
pase 関数は長すぎて載せないが、今回の例だとここ
return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil
defaultLocation は
jst, _ := time.LoadLocation("Asia/Tokyo")
なので、jst がセットされている。なのになぜ変わらないのだろう。
Date() のなかでも loc は入っている。
そして Date() のなかで setLoc() もしている
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time { if loc == nil { panic("time: missing Location in call to Date") } ... t := unixTime(unix, int32(nsec)) t.setLoc(loc) return t }
腑に落ちない…