目次
GO言語のtimeと拡張ライブラリ「carbon」
GO言語のtimeは日付と時刻をワンセットで扱うので日付だけの比較とか、日数計算とかをするとき若干面倒だったりします。
そのため、よく使う日付の操作を標準の「time」パッケージと、拡張ライブラリ「carbon」を比較しつつ整理しておこうという試みです。
carbonのインストール
VSCode(Visual Studio Code)のターミナルで以下を実行します。
go get github.com/uniplaces/carbon
上記のメッセージでOKです。
使うときは以下のように「time」と「carbon」をimportします。
package main import ( "fmt" "time" "github.com/uniplaces/carbon" )
以降、整理をしていきます。
使う頻度の高い日付操作とやり方の比較
timeとcarbonの両方のやり方を確認しますが、それが(ほぼ同じ)なのか(違う)のかは、小見出しの横に書いていきます。
日付・時刻の文字列変換(ほぼ同じ)
現在日付を文字列に変換します。
標準パッケージ「time」の例です。
var dt = time.Now() s1 := dt.String() var layout1 = "2006-01-02 15:04:05" s2 := dt.Format(layout1) var layout2 = "2006/01/02" s3 := dt.Format(layout2) var layout4 = "2006年01月02日 15時04分05秒" s4 := dt.Format(layout4)
上記のs1~s4のそれぞれの出力例です。
s1 : 2021-04-11 19:57:18.4928212 +0900 JST m=+0.006097501
s2 : 2021-04-11 19:57:18
s3 : 2021/04/11
s4 : 2021年04月11日 20時17分12秒
今度はcarbonの例です。
now := carbon.Now() s1 := now.String() var layout1 = "2006-01-02 15:04:05" s2 := now.Format(layout1) var layout2 = "2006/01/02" s3 := now.Format(layout2) var layout4 = "2006年01月02日 15時04分05秒" s4 := now.Format(layout4)
Now()がcarbonになっても、ほぼ同じやり方です。
出力フォーマットレイアウトは「2006-01-02 15:04:05」等日付時刻で指定します。
この日付・時刻の数字は変更できません。
例えば「2020-01-01 01:01:01」などにすると結果が無茶苦茶になります。
この日付・時刻の数字に意味があるからです。
理由は、GO言語の資料に、以下のように書いてありました。
The reference time used in the layouts is the specific time:
Mon Jan 2 15:04:05 MST 2006
which is Unix time 1136239445. Since MST is GMT-0700レイアウトで使用される参照時間は、特定の時間です。
月1月2日15:04:05MST 2006
これはUnix時間1136239445です。
日本語の曜日表示(ほぼ同じ)
日付の後ろに日本語の曜日を表示するパターンです。
日本語の曜日名の配列を用意して、WeekDay()で「日曜=0始まり」の数字を添え字に使います。
weekDays := []string{"日", "月", "火", "水", "木", "金", "土"} var layout = "2006/01/02" dt, _ := time.Parse(layout, "2021/04/11") if !dt.IsZero() { s3 := dt.Format(layout) fmt.Println(s3 + "(" + weekDays[dt.Weekday()] + ")") }
これで
2021/04/11(日)
と出力します。
carbonでもほぼ同じです。
weekDays := []string{"日", "月", "火", "水", "木", "金", "土"} var layout2 = "2006/01/02" now2, _ := carbon.CreateFromFormat(layout2, "2021/04/15", "Asia/Tokyo") if !now2.IsZero() { fmt.Println(now2.Format(layout2) + "(" + weekDays[now2.Weekday()] + ")") }
timeとの親和性を意識して作ってあるのがよくわかります。
日付・時刻の取得・作成(ほぼ同じ)
年月日時刻を数字指定するパターンと、日付文字列から生成するパターンです。
標準の「time」を使ったパターンです。
var jst = time.FixedZone("Asia/Tokyo", 9*60*60) var dt = time.Date(2021, 4, 11, 0, 0, 0, 0, jst) var layout2 = "2006/01/02" dt2, _ := time.Parse(layout2, "2021/05/01") if !dt2.IsZero() { s3 := dt2.Format(layout2) fmt.Println(s3) }
GO言語は、日本ローカルで動かすと自動的にJSTになってるそうですが、引数でJSTを省略することはできません。
変換に成功したかどうかは「!IsZero()」でチェックできます。
今度はcarbonです。
dt, _ := carbon.Create(2021, 4, 15, 0, 0, 0, 0, "Asia/Tokyo") var layout1 = "2006-01-02 15:04:05" dt2, _ := carbon.CreateFromFormat(layout1, "2021-04-15 00:00:00", "Asia/Tokyo") if !dt2.IsZero() { s3 := dt2.Format(layout1) fmt.Println(s3) }
IsZero()でチェックするのも 含めて、これも、ほぼ、同じ感じです。
日付のみの一致確認(ほぼ同じ)
よく使うのが「同じ日付か」とか「どちらがより未来か」などの日付比較です。
自分では、時刻レベルまで比較するということは、ほとんどありません。
timeは日付・時刻がセットなので、単純にEqualsで比較すると時刻が違うと同じ日付でも「違う」と判断されてしまうので、日付だけを比較したいなら、時刻を0クリアした日付に変換する必要があります。
(ちなみに、Now()=2021年4月16日にやってます)
dt := time.Now() var jst = time.FixedZone("Asia/Tokyo", 9*60*60) dt1 := time.Date(dt.Year(), dt.Month(), dt.Day(), 0, 0, 0, 0, jst) dt2 := time.Date(2021, 4, 16, 0, 0, 0, 0, jst) if dt1.Equal(dt2) { fmt.Println("match!") } else { fmt.Println("unmatch!") }
そこはcarbonでも同じです。
tmp, _ := carbon.Today("Asia/Tokyo") now, _ := carbon.Create(tmp.Year(), tmp.Month(), tmp.Day(), 0, 0, 0, 0, "Asia/Tokyo") now11, _ := carbon.Create(2021, 4, 16, 0, 0, 0, 0, "Asia/Tokyo") if now.Eq(now11) { fmt.Println("match!") } else { fmt.Println("unmatch!") }
日付のみの大小比較(違いあり)
Equals同様に、時刻を0クリアした日付に変換して比較します。
timeとcarbonではやり方に違いがあります。
まずは標準のtimeです。
Before(より前)・After(より後)を使います。
dt1には、2021/04/16、dt2には2021/04/20、dt3に2021/04/25をセットした想定です。
if dt1.Before(dt2) { fmt.Println("OK") } else { fmt.Println("woop!") } if dt3.After(dt2) { fmt.Println("OK") } else { fmt.Println("woop!") }
carbonは以下の一通りが正式名と短縮名でそろってます。
圧倒的にわかりやすいです。
- より小さい: LessThan または Lt
- 以下:LessThanOrEqualTo または Lte
- より大きい:.GreaterThan または Gt
- 以上:GreaterThanOrEqualTo または Gte
- AとBの間:Between(A,B,true)
サンプルです。
cdt1には、2021/04/16、cdt2には2021/04/20、cdt3に2021/04/25の想定です。
if cdt1.LessThan(cdt2) { fmt.Println("OK1") } if cdt1.Lt(cdt2) { fmt.Println("OK1t") } if cdt1.LessThanOrEqualTo(cdt2) { fmt.Println("OK2") } if cdt1.Lte(cdt2) { fmt.Println("OK2t") } if cdt2.GreaterThan(cdt1) { fmt.Println("OK3") } if cdt2.GreaterThanOrEqualTo(cdt1) { fmt.Println("OK4") } if cdt2.Gte(cdt1) { fmt.Println("OK4t") } if cdt2.Between(cdt1, cdt3, true) { fmt.Println("OK5") }
ある日を起点にした日付の計算
1年後、1月後、7日後などを計算してみます。
AddDate(年,月,日)を使います。
これは、timeでもcarbonでも全く同じことができます。
1年後
dtb := dtj.AddDate(1, 0, 0)
1月後
dtc := dtj.AddDate(0, 1, 0)
7日後
dta := dtj.AddDate(0, 0, 7)
これを使って計算した結果の例です。
基準日:2021-04-12
1年後:2022-04-12
1月後:2021-05-12
7日後:2021-04-19
マイナスを与えると、1年前、1月前、7日前とかにできます。
1年前
dtb := dtj.AddDate(-1, 0, 0)
1月前
dtc := dtj.AddDate(0, -1, 0)
7日前
dta := dtj.AddDate(0, 0, -7)
これを使って計算した結果の例です。
基準日:2021-04-12
1年前:2020-04-12
1月前:2021-03-12
7日前:2021-04-05
carbonでは上記と同じことを、別のやり方でもできます。
acdt1 := cdt1.AddYears(1) // 1年後
acdt2 := cdt1.AddMonths(1) // 1月後
acdt3 := cdt1.AddDays(7) // 7日後acdt1 := cdt1.AddYears(-1) // 1年前
acdt2 := cdt1.AddMonths(-1) // 1月前
acdt3 := cdt1.AddDays(-7) // 7日前
月末日を取得(違いあり)
今月末を求めてみます。
timの場合は、本日日付を取得(Now())して、その月+1(来月)1日の日付の前日を計算する方法になります。
var dtj = time.Now() var jst = time.FixedZone("Asia/Tokyo", 9*60*60) dt_first := time.Date(dtj.Year(), dtj.Month()+1, 1, 0, 0, 0, 0, jst) dt_lastday := dt_first.AddDate(0, 0, -1)
carbonだと、EndOfMonth()一発でできます。
今月末なら。
dt := carbon.Now().EndOfMonth()
です。
あと、carbonには、バリエーションとして,こんなのがあります。
fmt.Println("今10年の末日:" + carbon.Now().EndOfDecade().String())
fmt.Println("今四半期末日:" + carbon.Now().EndOfQuarter().String())
fmt.Println("今週末日" + carbon.Now().EndOfWeek().String())
fmt.Println("今年末日:" + carbon.Now().EndOfYear().String())
fmt.Println("今世紀末日:" + carbon.Now().EndOfCentury().String())
試しに、2021年4月16日に実行した結果は以下です。
今10年の末日:2029-12-31 23:59:59
今四半期末日:2021-06-30 23:59:59
今週末日2021-04-18 23:59:59
今年末日:2021-12-31 23:59:59
今世紀末日:2099-12-31 23:59:59
今週末は「日曜日」の日付をとってきてます。
EndOfをStartOfに置き換えるとそれぞれの初日もとれます。
日付の差分をとる(違いあり)
「指定日時点の年齢を計算」など、日付同志の差分をとるやり方の確認です。
timeにもdiff()はありますが、時間単位での差分がとれてしまいます。
これはcarbonの独壇場な気がします。
carbonの場合であれば、DiffDayなど日付の差分をとる関数が用意されています。
fmt.Printf("日付の差(日):%d日\n", acdt1.DiffInDays(cdt1, true))
fmt.Printf("日付の差(月):%d月\n", acdt1.DiffInMonths(cdt1, true))
fmt.Printf("日付の差(年):%d年\n", acdt1.DiffInYears(cdt1, true))
この結果は、以下のようになります。
日付の差(日):365日
日付の差(月):12月
日付の差(年):1年
とりあえず、個人的に使用頻度が高い操作はこんなところです。
ではでは。