"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

GO言語(golang)/使用頻度が高い日付操作で「time」と「carbon」を使い比べる。

f:id:arakan_no_boku:20210412005751p:plain

目次

 よく使う日付だけの操作

GO言語のtimeは日付と時刻をワンセットで扱うので日付だけの比較とか、日数計算とかをするとき若干面倒だったりします。

そのため、よく使う日付の操作を標準の「time」パッケージと、拡張ライブラリ「carbon」を比較しつつ整理しておこうという試みです。

github.com

carbonのインストール

VSCodeVisual Studio Code)のターミナルで以下を実行します。

go get github.com/uniplaces/carbon

f:id:arakan_no_boku:20210415221752p:plain

上記のメッセージで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年

とりあえず、個人的に使用頻度が高い操作はこんなところです。

ではでは。