【Go】now で日付操作

2 min read読了の目安(約2400字

はじめに

Go では標準ライブラリとして time が用意されており、日付や時間関連の大体の操作はサードパーティのライブラリを使わなくても問題なく実装できます。

https://golang.org/pkg/time/

しかし、細かな操作をしようと思うと可読性の落ちる書き方になりがちです。
次月末だとか本年の最終日だとかを取得するとなるとちょっと読みにくくなってきます。

// 今
now := time.Now() // 2021-05-02 01:26:03.313326 +0900 JST m=+0.000227513
// 明日
now.AddDate(0, 0, 1) // 2021-05-03 01:26:03.313326 +0900 JST
// 次月末
time.Date(now.Year(), now.Month()+2, 0, 0, 0, 0, 0, time.Local) // 2021-06-30 00:00:00 +0900 JST
// 本年の最終日最終時間
time.Date(now.Year()+1, 1, 0, 0, 0, 0, 0, time.Local) // 2021-12-31 00:00:00 +0900 JST

引数が多いですね。
あと日付以降で0が連続していて何だか嫌な気分です。思考停止で書いていると間違えそうです。

ちなみにtime.Date()の第二引数のtime.Monthは13以上のintが入ると、その数に応じて年を繰り上げてくれます。

now := time.Now() // 2021-05-02 01:26:03.313326 +0900 JST m=+0.000227513
time.Date(now.Year(), 13, 1, 1, 0, 0, 0, time.Local) // 2022-01-01 01:00:00 +0900 JST

jinzhu/now

https://github.com/jinzhu/now/tree/e8c28ddddc015eb8c0a5781a7428cc0bc1b2a5c9

gorm、ginなどで有名なjinzhuさんのnowにはやや細かな日付を操作する為の関数が用意されています。
いくつかの関数を紹介してみます。

BeginningOfDay()

今日の日付の00時00分00秒を取得します。

time.Now() // 2021-05-02 01:26:03.313326 +0900 JST m=+0.000227513
now.BeginningOfDay() // 2021-05-02 00:00:00 +0900 JST

BeginningOfYear()

本年の初日の00時00分00秒を取得します。

time.Now() // 2021-05-02 01:26:03.313326 +0900 JST m=+0.000227513
now.BeginningOfYear() // 2021-01-01 00:00:00 +0900 JST

EndOfMonth()

今月末日を取得します。
時刻は次月の初日から1nanosecond戻した表示になっています。

time.Now() // 2021-05-02 01:26:03.313326 +0900 JST m=+0.000227513
now.EndOfMonth() // 2021-05-31 23:59:59.999999999 +0900 JST

EndOfWeek()

今週末日を取得します。
時刻は次週の初日から1nanosecond戻した表示になっています。

time.Now() // 2021-05-02 01:26:03.313326 +0900 JST m=+0.000227513
now.EndOfWeek() // 2021-05-08 23:59:59.999999999 +0900 JST

デフォルトは週の初日がSundayとなっていますが、変更することも可能です。

time.Now() // 2021-05-02 01:26:03.313326 +0900 JST m=+0.000227513
now.WeekStartDay = time.Monday // 週の初日をMondayに変更
now.EndOfWeek() // 2021-05-09 23:59:59.999999999 +0900 JST

With()

上記の関数を呼び出した際には、内部でtime.Nowが呼ばれているので現在時刻から算出された日付が返されるのですが、
With()を用いることで指定した日付から算出するように設定できます。

// 明日の00時00分00秒を取得
tomorrow := time.Now().AddDate(0,0,1)
now.With(tomorrow).BeginningOfDay() // 2021-05-03 00:00:00 +0900 JST

所感

日付操作は読み手に優しくないコードになりがちだと思っています。
ここにさらにタイムゾーンの変換等が入ると、かなり苦しいコードリーディングになってしまいます。
今回紹介したnowを用いれば、随分すっきりとしたコードで日付操作を実装できるのではないでしょうか。

またライブラリをプロジェクトに導入する際には、実装を全て読むとまではいきませんが、仕様を理解した上で導入するべきです。
ふわっとI/Oだけを見てなんとなくで使用するとどこかで痛い目に会いますよ!(自戒)