Golangでのtime packageのいろんな使い方まとめました

# 概要
golangでtime objectを利用してあれこれデータを操作したりすることがあり、その時に調べたことややったことをまとめました。

# 目次
- stringからtime.Timeへ変換
- stringからtime.Timeへ変換(Timezoneを考慮)
- time型objectのTimeZoneのみを変換する
- 日付から特定のTimzoneの00:00:00を作る
- 〇〇前、〇〇後の時刻を取得する
- 2つの時刻を比較する (差分、Before、After、Since、Until)
- 2つの時刻から間の日付リストを作る
- 2つの時刻からその間の特定の曜日の日付リストを作る

# stringからtime.Timeへ変換
```golang:string-to-time.go

dateLayout := "2006-01-02"
dateTimeLayout := "2006-01-02 15:04:05"
dateTarget := "2020-01-28"
dateTimeTarget := "2020-01-28 13:14:15"

dateObject, _ := time.Parse(dateLayout, dateTarget) // 2020-01-28 00:00:00 +0000 UTC
dateTimeObject, _ := time.Parse(dateTimeLayout, dateTimeTarget) // 2020-01-28 13:14:15 +0000 UTC
```
試したい方はこちら -> https://play.golang.org/p/o27qyHiuOob

ちなみに、time packageを使うにあたって、time.Formatやtime.Parseを使う際のLayoutは上記以外機能してくれませんので注意！
ちなみになぜこの並びかと言うと、 MST(米国山岳部標準時)になおすとあら不思議、綺麗な数字並びになっているからなんです。

>Mon Jan 2 15:04:05 MST 2006
>01/02 03:04:05PM '06 -0700

https://golang.org/pkg/time/#pkg-constants

# stringからtime.Timeへ変換(Timezoneを考慮)
Parse, ParseInLocationを使う

```golang:string-to-timeWithTimezone.go
// 日付だけの場合は、defaultでUTCの00:00:00になる
targetDate := "2020-03-09"
date, _ := time.Parse("2006-01-02", targetDate) // 2020-03-09 00:00:00 +0000 UTC

// 日付だけでTimeZoneを指定した場合は、指定したTimeZoneの00:00:00になる
targetTimezone := "Asia/Tokyo"
tzloc, _ := time.LoadLocation(targetTimezone)
dateWithTimezone, _ := time.ParseInLocation("2006-01-02", targetDate, tzloc) // 2020-03-09 00:00:00 +0900 JST

// 日時だけの場合(TimeZoneなし)は、defaultでUTCになる
targetDT := "2020-03-09T09:00:00"
dateTime, _ := time.Parse("2006-01-02 15:04:05", targetDT) // 2020-03-09 09:00:00 +0000 UTC

// 日時だけの場合(TimeZoneなし)でTimeZoneを指定した場合は、指定したTimeZoneが付加される
dateTimeSetTZ, _ := time.ParseInLocation("2006-01-02 15:04:05", targetDT, tzloc) // 2020-03-09 09:00:00 +0900 JST

// 日時とTimeZoneがある場合は、そのままtime型に変換されるだけ
targetDTWithTZ := "2020-03-09T09:00:00+05:00"
dateTimeWithTZ, _ := time.Parse(time.RFC3339, targetDTWithTZ) // 2020-03-09 09:00:00 +0500 +0500

// 日時とTimeZoneがある場合は、TimeZoneは変換されない！
// TimeZoneだけを変換したい場合はInを使おう。詳細は次のセクション
dateTimeWithTZSetTZ, _ := time.ParseInLocation(time.RFC3339, targetDTWithTZ, tzloc) // 2020-03-09 09:00:00 +0500 +0500
```
試したい方はこちら -> https://play.golang.org/p/cTIwh-vp2Q1

# time型objectのTimeZoneのみを変換する
Inを使う

```golang:change-timezone.go
targetDT := "2020-03-09T09:00:00+05:00"
targetTZ := "Asia/Tokyo"
tzloc, _ := time.LoadLocation(targetTZ)

dt, _ := time.Parse(time.RFC3339, targetDT) // 2020-03-09 09:00:00 +0500 +0500
dtConvertTZ := dt.In(tzloc) // 2020-03-09 13:00:00 +0900 JST
```
試したい方はこちら -> https://play.golang.org/p/baSzGM4pftJ

# 日付から特定のTimzoneの00:00:00を作る
```golang:date-to-dateTimeMidnightWithTimezone.go
target := "2020-01-28"
targetTimezone := "Asia/Tokyo"
date, _ := time.Parse("2006-01-02", target) // 2020-01-28 00:00:00 +0000 UTC

tzloc, _ := time.LoadLocation(targetTimezone)

dateTimeMidnight := time.Date(date.Year(),
	date.Month(),
	date.Day(),
	0, 0, 0, 0, tzloc) // 2020-01-28 00:00:00 +0900 JST
```
試したい方はこちら -> https://play.golang.org/p/E7je0DSAqiW

# 〇〇前、〇〇後の時刻を取得する
AddまたはAddDateを使います。前か後かは正の数か負の数で表す。

```golang:increase-reduce-time.go
dtString := "2020-03-05T15:10:47+09:00"
dt, _ := time.Parse(time.RFC3339, dtString)
// 1分後
t1 := dt.Add(1 * time.Minute) // 2020-03-05 15:11:47 +0900 +0900
// 1時間後
t2 := dt.Add(1 * time.Hour) // 2020-03-05 16:10:47 +0900 +0900
// 1年2ヶ月3日後
t3 := dt.AddDate(1, 2, 3) // 2021-05-08 15:10:47 +0900 +0900

// 1分前
t4 := dt.Add(-1 * time.Minute) // 2020-03-05 15:09:47 +0900 +0900
// 1時間前
t5 := dt.Add(-1 * time.Hour) // 2020-03-05 14:10:47 +0900 +0900
// 1年2ヶ月3日前
t6 := dt.AddDate(-1, -2, -3) // 2019-01-02 15:10:47 +0900 +0900
```
試したい方はこちら -> https://play.golang.org/p/pZahLsfwesB

# 2つの時刻を比較する (差分、Before、After、Since、Until)
Sub, After, Before, Since, Untilを使う

```golang:time-comparizon.go
t1String := "2020-02-01T13:14:15+09:00"
t2String := "2020-02-10T13:14:15+09:00"
t1, _ := time.Parse(time.RFC3339, t1String)
t2, _ := time.Parse(time.RFC3339, t2String)
// t1 - t2
duration1 := t1.Sub(t2) // -216h0m0s
// (t2 - t1)を日数に計算する
duration2 := int(t2.Sub(t1).Hours()) / 24 // 9
// 今はt1からどれだけ経過しているか (time.Now().Sub(t1)と同じ. now - t1)
time.Since(t1)
// t1は今からどれだけ経過しているか (t1.Sub(time.Now())と同じ. t1 - now)
time.Until(t1)
// t1はt2より後か
t1.After(t2) // false
// t1はt2より前か
t1.Before(t2) // true
```
試したい方はこちら -> https://play.golang.org/p/zLshTmUKVyj

# 日時からその日の00:00:00を作る

```golang:dateTime-to-dateTimeMidnightWithTimezone.go
dateTimeTarget := "2020-01-28T13:14:15+09:00"
dateTime, _ := time.Parse(time.RFC3339, dateTimeTarget) // 2020-01-28 13:14:15 +0900 +0900

dateTimeMidnight := time.Date(dateTime.Year(),
	dateTime.Month(),
	dateTime.Day(),
	0, 0, 0, 0, dateTime.Location()) // 2020-01-28 00:00:00 +0900 +0900
```
試したい方はこちら -> https://play.golang.org/p/kzaf6x2gX4d

#2つの時刻から間の日付リストを作る

```
fromString := "2020-02-01T13:14:15+09:00"
toString := "2020-02-05T13:14:15+09:00"
from, _ := time.Parse(time.RFC3339, fromString)
to, _ := time.Parse(time.RFC3339, toString)

dateDuration := int(to.Sub(from).Hours()) / 24 + 1 // 5
	
var dates []string
for i := 0; i < dateDuration; i++ {
	date := from.AddDate(0, 0, i).Format("2006-01-02")
	dates = append(dates, date)
}

// dates: [2020-02-01 2020-02-02 2020-02-03 2020-02-04 2020-02-05]
```

試したい方はこちら -> https://play.golang.org/p/sNcbcFET2KV

# 2つの時刻からその間の特定の曜日の日付リストを作る
```golang:dateList-dependingOf-dayOfWeek.go
fromString := "2020-02-01T13:14:15+09:00"
toString := "2020-02-10T13:14:15+09:00"
from, _ := time.Parse(time.RFC3339, fromString)
to, _ := time.Parse(time.RFC3339, toString)
targetDayOfWeek := []string{"Monday", "Tuesday", "Wednesday"}

dateDuration := int(to.Sub(from).Hours()) / 24 + 1 // 10
	
var dates []string
for i := 0; i < dateDuration; i++ {
	date := from.AddDate(0, 0, i)
	if isValidDayOfWeek(date, targetDayOfWeek) {
		dates = append(dates, date.Format("2006-01-02"))
	}
}

// dateの曜日が特定の曜日に含まれるかCheck
func isValidDayOfWeek(date time.Time, dayOfWeek []string) bool {
	for _, dow := range dayOfWeek {
		if date.Weekday() == getIntDayOfWeek(dow) {
			return true
		}
	}
	return false
}

// 英語の曜日(string)をWeekday型に変換
func getIntDayOfWeek(day string) time.Weekday {
	switch day {
	case "Sunday":
		return time.Sunday
	case "Monday":
		return time.Monday
	case "Tuesday":
		return time.Tuesday
	case "Wednesday":
		return time.Wednesday
	case "Thursday":
		return time.Thursday
	case "Friday":
		return time.Friday
	case "Saturday":
		return time.Saturday
	}
	return time.Saturday
}

// dates: [2020-02-03 2020-02-04 2020-02-05 2020-02-10] 
```
試したい方はこちら -> https://play.golang.org/p/y6AOBbp5hdG