CUBE SUGAR CONTAINER

技術系のこと書きます。

GNU date で月末の日付を得る

今回は、GNU date を使って月末の日付を得る方法について。 シェルスクリプトで一ヶ月単位の処理を書こうとすると、よく調べることになるのでメモしておく。

検証に使った環境は次のとおり。

$ sw_vers                                        
ProductName:    Mac OS X
ProductVersion: 10.15.7
BuildVersion:   19H15
$ date --version
date (GNU coreutils) 8.32
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by David MacKenzie.

もくじ

下準備

検証に使ったのは macOS で、デフォルトでは date(1) が BSD date になっている。 GNU date の方がオプションが多彩で扱いやすいので入れ替えよう。 Homebrew で Coreutils を入れると gdate というコマンドで GNU date が使えるようになる。

$ brew install coreutils
$ alias date=gdate

GNU date の使い方

GNU date では + 以降にフォーマット文字列を指定することで日時を整形できる。 使えるフォーマット文字列は man 1 gdate を参照のこと。

たとえば、次のようにすれば年月日をハイフンでつないだフォーマットで今日の日付を表示できる。

$ date '+%Y-%m-%d'                 
2020-11-13

先ほどの実行結果を見てわかるとおり、デフォルトでは表示される内容として現在の日時が使われる。 これは --date オプションに now を指定していることに等しい。

$ date '+%Y-%m-%d' --date 'now'    
2020-11-13

--date オプションは Human readable なフォーマットを受け付けるようになっている。 たとえば 1 month を指定すると、現在の 1 ヶ月後になる。

$ date '+%Y-%m-%d' --date '1 month'
2020-12-13

他にも、1 day agoyesterday とすれば昨日が得られる。

$ date '+%Y-%m-%d' --date '1 day ago'
2020-11-12
$ date '+%Y-%m-%d' --date 'yesterday'
2020-11-13

それ以外のテクニックとして、日時をずらした上でフォーマットを駆使して意図した内容を得ることもできる。 たとえば、月初の日付がほしいなら、フォーマットの日付を 1 日にしてやれば良い。 以下では、来月の 1 日の日付を取得している。

$ date '+%Y-%m-01' --date '1 month'
2020-12-01

月末の日付を得る

さて、それでは今回の本題に入る。 たとえば、ここまでの要領で「今月末の日付」を得るには「来月の 1 日の 1 日前」を計算してやれば良いことになる。 これを一回の date(1) の実行で得るのは難しいので、インラインコマンドを駆使する。 つまり、まずは「来月の 1 日」を得た上で、それを次の date(1) の --date オプションに埋め込んでやる。

$ date '+%Y-%m-%d' --date "1 day ago $(date '+%Y-%m-01' --date '1 month')"
2020-11-30

ちゃんと今月末の日付が得られた。

次に、任意の月の月末の日付がほしいなら、インラインで実行する date(1) の --date オプションにその月の日付を指定すれば良い。 たとえば、以下では 2020 年 2 月の末日を取得している。 今年はうるう日があった。

$ date '+%Y-%m-%d' --date "1 day ago $(date '+%Y-%m-01' --date '1 month 2020-02-01')"
2020-02-29

めでたしめでたし。