Go/golangでの日付処理まとめ(チートシート)

この記事は[全部俺 Advent Calendar 2018](https://adventar.org/calendars/3702)の20日目の記事です。
Goの日付関連の処理についてまとめます。

# 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
```
参考：日付フォーマットの詳細については、[こちら](https://golang.org/src/time/format.go)を参照してください。
ちなみに、なぜフォーマットが`2006/01/02 15:04:05 MST`なのかというと、これは形式を変えて書く(アメリカ式の時刻の順番で書く)と`01/02 03:04:05PM '06 -0700`となるからだそうです。（MSTは山岳部標準時で`-0700`となるらしいです。）

### 雑にパースしたいとき/フォーマットが明確でないとき
```go
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で出力されていることに注意です。

## パース編(日付型 → 文字列型)
```go
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
```

## タイムゾーンの処理
```go
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 日付 - 期間
```go
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
```

### 月初・月末
```go
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`


## 週数の判定
```go
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
```

## 曜日の判定
```go
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系とは逆なのでしょっちゅう間違えます。頑張って慣れていきたい。

今後も思いついたら適宜追加していく予定です。
指摘・修正や編集リクエストもお待ちしております！