この記事は全部俺 Advent Calendar 2018の20日目の記事です。
Goの日付関連の処理についてまとめます。
Goの日付処理チートシート
パース編(文字列 → 日付型)
フォーマットが明確なとき
import "time"
time.Parse("20060102", "20181220") // => 2018-12-20 00:00:00 +0000 UTC
time.Parse("20060102150405", "20181220123456") // => 2018-12-20 12:34:56 +0000 UTC
time.Parse("2006/01/02 15:04:05.000", "2018/12/20 12:34:56.123") // => 2018-12-20 12:34:56.123 +0000 UTC
time.Parse("2006-01-02T15:04:05.000000Z", "2018-12-20T12:34:56.123456Z") // => 2018-12-20 12:34:56.123456 +0000 UTC
time.Parse("Mon, 02 Jan 2006 15:04:05 MST", "Thu, 20 Dec 2018 12:34:56 GMT") // => 2018-12-20 12:34:56 +0000 GMT
time.Parse("Mon Jan 2 15:04:05 MST 2006", "Thu Dec 20 12:34:56 JST 2018") // => 2018-12-20 12:34:56 +0900 JST
参考:日付フォーマットの詳細については、こちらを参照してください。
ちなみに、なぜフォーマットが2006/01/02 15:04:05 MST
なのかというと、これは形式を変えて書く(アメリカ式の時刻の順番で書く)と01/02 03:04:05PM '06 -0700
となるからだそうです。(MSTは山岳部標準時で-0700
となるらしいです。)
雑にパースしたいとき/フォーマットが明確でないとき
import "github.com/Songmu/go-httpdate"
httpdate.Str2Time("20181220", nil) // => 2018-12-20 00:00:00 +0900 JST
httpdate.Str2Time("20181220123456", nil) // => 2018-12-20 12:34:56 +0900 JST
httpdate.Str2Time("2018/12/20 12:34:56.123", nil) // => 2018-12-20 12:34:56.123 +0900 JST
httpdate.Str2Time("2018-12-20T12:34:56.123456Z", nil) // => 2018-12-20 12:34:56.123456 +0000 UTC
httpdate.Str2Time("Thu, 20 Dec 2018 12:34:56 GMT", nil) // => 2018-12-20 12:34:56 +0000 GMT
httpdate.Str2Time("Thu Dec 20 12:34:56 JST 2018", nil) // => 2018-12-20 12:34:56 +0900 JST
第2引数のloc(ロケーション)にnilを入れた場合、文字列でタイムゾーンが指定されていない場合はローカル環境に依存します。
ここではJSTで出力されていることに注意です。
パース編(日付型 → 文字列型)
import "time"
t := time.Date(2018, 12, 20, 12, 34, 56, 123456, time.UTC)
t.Format("20060102") // => 20181220
t.Format("20060102150405") // => 20181220123456
t.Format("2006/01/02 15:04:05.000") // => 2018/12/20 12:34:56.000
t.Format("2006-01-02T15:04:05.000000000Z") // => 2018-12-20T12:34:56.000123456Z
t.Format("Mon, 02 Jan 2006 15:04:05 MST") // => Thu, 20 Dec 2018 12:34:56 UTC
t.Format("Mon Jan 2 15:04:05 MST 2006") // => Thu Dec 20 12:34:56 UTC 2018
タイムゾーンの処理
import "time"
// ローカルでの現在時刻取得(デフォルトロケール:'ja_JP'での例)
time.now() // => 2018-12-20 22:43:49.764662 +0900 JST m=+0.000477630
time.Now().UTC() // => 2018-12-20 13:44:06.781869 +0000 UTC
// タイムゾーンの変換
// UTC → JST
t := time.Date(2018, 12, 20, 12, 34, 56, 123456, time.UTC) // => 2018-12-20 12:34:56.000123456 +0000 UTC
loc, err := time.LoadLocation("Asia/Tokyo")
if err != nil {
loc = time.FixedZone("Asia/Tokyo", 9*60*60)
}
t = t.In(loc) // => 2018-12-20 21:34:56.000123456 +0900 JST
// JST → UTC
t.UTC() // => 2018-12-20 12:34:56.000123456 +0000 UTC
演算編
日付 + 期間 or 日付 - 期間
import "time"
t := time.Date(2018, 12, 20, 12, 34, 56, 123456, time.UTC) // => 2018-12-20 12:34:56.000123456 +0000 UTC
t.AddDate(0, 0, 1) // => 2018-12-21 12:34:56.000123456 +0000 UTC
t = time.Date(2018, 12, 20, 12, 34, 56, 123456, time.UTC) // => 2018-12-20 12:34:56.000123456 +0000 UTC
t.AddDate(0, 0, -1) // => 2018-12-19 12:34:56.000123456 +0000 UTC
t = time.Date(2018, 12, 20, 12, 34, 56, 123456, time.UTC) // => 2018-12-20 12:34:56.000123456 +0000 UTC
t.AddDate(1, 1, 1) // => 2020-01-21 12:34:56.000123456 +0000 UTC
月初・月末
import "time"
// 今月の月初
time.Date(2018, time.Now().Month(), 1, 0, 0, 0, 0, time.Local) // => 2018-12-01 00:00:00 +0900 JST
// 今月の月末
time.Date(2018, time.Now().Month(), 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, -1) // => 2018-12-31 00:00:00 +0900 JST
// 先月の月初/月末
time.Date(2018, time.Now().Month() - 1, 1, 0, 0, 0, 0, time.Local) // => 2018-11-01 00:00:00 +0900 JST
time.Date(2018, time.Now().Month() - 1, 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, -1) // => 2018-11-30 00:00:00 +0900 JST
// 来月の月初/月末
time.Date(2018, time.Now().Month() + 1, 1, 0, 0, 0, 0, time.Local) // => 2018-11-01 00:00:00 +0900 JST
time.Date(2018, time.Now().Month() + 1, 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, -1) // => 2018-11-30 00:00:00 +0900 JST
ちなみにmonth
に13を入れても問題なく動作するので、12月でも大丈夫です。
time.Date(2018, 13, 1, 0, 0, 0, 0, time.Local) => 2019-01-01 00:00:00 +0900 JST
週数の判定
import "time"
t := time.Date(2018, 12, 20, 12, 34, 56, 123456, time.UTC) // => 2018-12-20 12:34:56.000123456 +0000 UTC
# その日が年の何周目にあたるのか(月曜始まり)
y, w := t.ISOWeek() // y => 2018, w => 51
// その日が月の何週目にあたるのか(月曜始まり)
// 月初日の週数を出して減算する
_, firstW := time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()).ISOWeek()
w - firstW + 1 // => 4
曜日の判定
import "time"
t := time.Date(2018, 12, 20, 12, 34, 56, 123456, time.UTC) // => 2018-12-20 12:34:56.000123456 +0000 UTC
// 日曜始まりの日本語定数リストを作成
jweek := [7]string{"日", "月", "火", "水", "木", "金", "土"}
t.Weekday() // => Thursday
jweek[t.Weekday()] // => 木
まとめ
Goの文字列日付パースは独特の記法なので慣れが必要そうです。
先にフォーマットの方から記載するのもPython3系とは逆なのでしょっちゅう間違えます。頑張って慣れていきたい。
今後も思いついたら適宜追加していく予定です。
指摘・修正や編集リクエストもお待ちしております!