※Goのバージョンは1.9.1で確認しています。
Go言語で特定の文字列から時刻(Time型)を生成する際には、パッケージ「time」の「Parse」や「ParseInLocation」を利用します。
違いは、ParseがUTC固定で、ParseInLocationがLocation(「”Asia/Tokyo”」等)を指定できる点です。
また、どちらも第1引数に「layout」というフォーマット用の文字列を指定しますが、他の言語のように「%Y%m%d」な表記ではなく「2006-01-02 15:04:05」のような特定の値を指定する必要があります。
残念ながら、この値を利用者が好きな値を変える(例えば、layoutの指定文字を「2018-08-31 21:22:23」にする)ことはできません。
ここでハマりどころなのは、ちょっとでも書式に合わない文字列を指定すると、「0001-01-01 00:00:00 +0000 UTC」を返してくる点です。
原因としては、以下の2点があります。
layoutの指定が悪い場合:
序盤にハマりやすいケースです。主に動作検証をしている時に遭遇します。
上述したように、第1引数の「layout」は特定の値で指定する必要があります。
最初に挙げた、「2006-01-02 15:04:05」を例にとってみます。
この場合ですと、「2006」が「年」を4桁で、「01」が「月」を、「02」が「日」を、「15」が「時」を24時表記で、「04」が「分」を、「05」が「秒」を表します。(ちなみに、「時」を12時表記にしたい場合には「03」となります。)
また、別の例として「2006-01-02T15:04:05.000 -07:00」を挙げます。
年月日時分秒の部分は「2006-01-02 15:04:05」と同じです。違いとして、タイムゾーン指定としての「T」、ミリ秒の「000」、UTCとの差分時間として「-07:00」を表しています。
なお、「何故この値なのか」についてはオンラインマニュアルに記載されています。
https://golang.org/pkg/time/#pkg-constants
また、Qiitaで日本語で説明されているページもあります。
qiita.com
指定するlayout用の文字列については、ひとまず日本でよく使われる形式である、以下の3つを覚えておけば差し支えないかと思います。
- 「2006-01-02」:年月日のみの指定。時刻は00:00:00.000
- 「2006-01-02 15:04:05」:年月日時刻秒の指定。ミリ秒は000
- 「2006-01-02T15:04:05.000 -07:00」:年月日時刻秒とミリ秒、タイムゾーンの指定。
以下、いくつか実行例を挙げます。
例では、「Parse」で実施していますが、「ParseInLocation」でも同じです。
まずは、簡単な例でlayoutに「2006-01-02」と「2006-02-01」(月と日を入れ替えた)を指定した場合。
この場合、該当する箇所の値として処理できる場合には変換はされますが、もちろん意図した値にはなりません。
t1, _ := time.Parse("2006-01-02", "2018-11-12") fmt.Println("t1-1:", t1) t2, _ := time.Parse("2006-02-01", "2018-11-12") fmt.Println("t1-2:(mm<-->dd)", t2) t3, _ = time.Parse("2006-02-01", "2018-11-13") fmt.Println("t1-3:(mm<-->dd)", t3)
以下、出力結果です。
t1-1: 2018-11-12 00:00:00 +0000 UTC t1-2:(mm<-->dd) 2018-12-11 00:00:00 +0000 UTC t1-3:(mm<-->dd) 0001-01-01 00:00:00 +0000 UTC
時分秒は指定がないため、「0」で出力されています。
なお、「t1-2」では、月と日をlayoutで逆に指定しているため、第二引数で渡した値の月と日の出力箇所が逆(「11-12」の予定が「12-11」)に出力されています。
また、「t1-3」でも同様に月と日をlayoutで逆に指定していますが、第二引数で渡した値の「日」の13が「月」の範囲を超えているため、「0001-01-01 00:00:00 +0000 UTC」で出力されています。
続いて、layoutに「2006-01-02 15:04:05」を指定する例です。
「時」と「分」の入れ替え、「03」指定の際に時に「13」と「12」を設定している場合の実行例です。
t4, _ := time.Parse("2006-01-02 15:04:05", "2018-11-12 13:14:15") fmt.Println("t4:", t4) t4, _ = time.Parse("2006-01-02 15:05:04", "2018-11-12 13:14:15") fmt.Println("t4-1(mi<->ss):", t4) t4, _ = time.Parse("2006-01-02 03:04:05", "2018-11-12 13:14:15") fmt.Println("t4-2(mi->am/pm):", t4) t4, _ = time.Parse("2006-01-02 03:04:05", "2018-11-12 12:14:15") fmt.Println("t4-3(mi->am/pm 12):", t4)
以下、出力結果です。
t4: 2018-11-12 13:14:15 +0000 UTC t4-1(mi<->ss): 2018-11-12 13:15:14 +0000 UTC t4-2(mi->am/pm): 0001-01-01 00:00:00 +0000 UTC t4-3(mi->am/pm 12): 2018-11-12 12:14:15 +0000 UTC
「4-1」では「分」と「秒」が入れ替わっています。
「4-2」では、12時表記の箇所に変換可能範囲を超える「13」を渡してしまったので書式にあっていないと判断されて「0001-01-01 00:00:00 +0000 UTC」になってしまっています。
「4-3」では、12時表記の箇所に変換可能範囲内の「12」を渡してしているので値がそのまま変換できています。
どうしてこういう動きになるかの詳細については、オンラインマニュアルやソースコードを直接確認した方が理解が進むと思います。
https://golang.org/src/time/format.go
変換対象の文字列が悪い場合:
こちらは、どちらかというと実行時に発生しやすいかも知れません。
この場合の対処は簡単です。
「layaout」で定義した書式と同じ書式で変換したい文字列を、第2引数「value」へ指定してください。
layout9 := "2006-01-02T15:04:05.000000 -07:00" t9, _ := time.Parse(layout9, "2018-11-12T13:14:15.000010 +09:00") fmt.Println("t9-1:", t9) t9, _ = time.Parse(layout9, "2018-11-12T13:14:15.00001 +09:00") fmt.Println("t9-2:", t9) t9, _ = time.Parse(layout9, "2018-11-12T13:14:15.000010 09:00") fmt.Println("t9-3:", t9)
上記の実行結果は以下になります。
t9-1: 2018-11-12 13:14:15.00001 +0900 JST t9-2: 0001-01-01 00:00:00 +0000 UTC t9-3: 0001-01-01 00:00:00 +0000 UTC
この例では、「t9-2」側はlayoutと比較してミリ秒の桁が1つ足り無いために、「0001-01-01 00:00:00 +0000 UTC」となっています。
また、「t9-3」側はUTCとの時差に「+記号」が無いために、やはり「0001-01-01 00:00:00 +0000 UTC」となっています。