先日、2017年02月24日よりプレミアムフライデーなるモノが始まった。 これは、毎月の最終金曜日は15時退社にして余暇を作ることで個人消費を増やし、景気促進を促すという経産省主導の消費喚起のプログラムなのだが、あいにくと導入している企業は0.1%前後とかで、今の時点だとプレミアムな企業にしか適用されてないようだ(プレミアムな企業だったら給料も高いので、消費喚起という意味ではまぁ間違ってない…のか?景気対策としての効果は疑問だけど、まぁ結果を見てからかなぁ…)。

で、じゃあ今年のプレミアムフライデーは何日なのかをコンソール上で出力させてってみよう。 日付関係の処理を行うといえばdateコマンドなので、今回はこれらを利用する。

1. Faketimeを用いる場合

さて、dateコマンドといえば、「-d」オプションで"last friday"とか指定すれば前回の金曜日の日付を取得してくれるのだが、この指定方法は日付を直接指定してから行えない。 つまり、現在時刻からみての前回の金曜日が出力されてしまう。

blacknon@BS-PUB-UBUNTU-01:~$ date -d 'last friday'
2017年  2月 24日 金曜日 00:00:00 JST
blacknon@BS-PUB-UBUNTU-01:~$ 
blacknon@BS-PUB-UBUNTU-01:~$ # 直接指定した日付が優先されてしまう
blacknon@BS-PUB-UBUNTU-01:~$ date -d '2017/02/01 last friday'
2017年  2月  1日 水曜日 00:00:00 JST

でも、この-dで前回の金曜日を取得したい…。 そんなときは、結構前にも触れたことのあるlibfaketimeをインストールするとよいだろう。 これを使うことで、dateコマンドが認識する時刻をfaketimeで指定した時刻にだますことが可能になる。 faketimeは前に紹介した時から結構進化しており、以下のようにyumやaptでインストールが可能となっている。

yum --enablerepo epel install libfaketime
apt install faketime
brew install libfaketime

で、あとはdateコマンドの実行前にfaketimeで適当な日付を指定してやる。

faketime 2017-01-01 date -d 'last friday'
[root@BS-PUB-CENT7-01 ~]# faketime 2017-01-01 date -d 'last friday'
2016年 12月 30日 金曜日 00:00:00 JST

これを応用してやれば、以下のように記述することで各月の最終金曜日、プレミアムフライデーを出力させることができる。

printf 'faketime %s date -d "last fri"\n' {17{02..12},1801}01|sh
[root@BS-PUB-CENT7-01 ~]# # ブレース展開はネストさせることができるので、これを利用して2017/02~2018/01の01日の日付を生成する
[root@BS-PUB-CENT7-01 ~]# printf "%s\n" 20{17/{02..12},18/01}/01
2017/02/01
2017/03/01
2017/04/01
2017/05/01
2017/06/01
2017/07/01
2017/08/01
2017/09/01
2017/10/01
2017/11/01
2017/12/01
2018/01/01
[root@BS-PUB-CENT7-01 ~]#
[root@BS-PUB-CENT7-01 ~]# # 上と組み合わせて、printfで実行するコマンドを一覧で出力させる
[root@BS-PUB-CENT7-01 ~]# printf 'faketime %s date -d "last fri"\n' 20{17/{02..12},18/01}/01
faketime 2017/02/01 date -d "last fri"
faketime 2017/03/01 date -d "last fri"
faketime 2017/04/01 date -d "last fri"
faketime 2017/05/01 date -d "last fri"
faketime 2017/06/01 date -d "last fri"
faketime 2017/07/01 date -d "last fri"
faketime 2017/08/01 date -d "last fri"
faketime 2017/09/01 date -d "last fri"
faketime 2017/10/01 date -d "last fri"
faketime 2017/11/01 date -d "last fri"
faketime 2017/12/01 date -d "last fri"
faketime 2018/01/01 date -d "last fri"
[root@BS-PUB-CENT7-01 ~]#
[root@BS-PUB-CENT7-01 ~]# # 生成したコマンドをパイプでシェルに渡して実行させる
[root@BS-PUB-CENT7-01 ~]# printf 'faketime %s date -d "last fri"\n' 20{17/{02..12},18/01}/01 | sh
2017年  1月 27日 金曜日 00:00:00 JST
2017年  2月 24日 金曜日 00:00:00 JST
2017年  3月 31日 金曜日 00:00:00 JST
2017年  4月 28日 金曜日 00:00:00 JST
2017年  5月 26日 金曜日 00:00:00 JST
2017年  6月 30日 金曜日 00:00:00 JST
2017年  7月 28日 金曜日 00:00:00 JST
2017年  8月 25日 金曜日 00:00:00 JST
2017年  9月 29日 金曜日 00:00:00 JST
2017年 10月 27日 金曜日 00:00:00 JST
2017年 11月 24日 金曜日 00:00:00 JST
2017年 12月 29日 金曜日 00:00:00 JST

2. Faketimeを用いない場合

さて、じゃあFaketimeを使わない場合だとどうすればよいのか。

dateでは出力フォーマットを指定できるので、それを使って月の金曜日で最後の日付を抽出してやればよい。 とりあえず、こんな感じ。

echo 17{12..01}{31..01}$'\n' | date -f - "+%F %m %w" 2>&- | awk '$3~ /5/ && !a[$2$3]++{print $1}' | tac

分解するとこんな感じ。

# ブレース展開で(存在しない日付を含む)1年間すべての日付を取得する
echo 2017/{12..01}/{31..01}$'\n'

# パイプで渡してやり、"YYYY-MM-DD MM 曜日(番号(金曜は5))"の形式で出力する。なお、「2>&-」は「2>/dev/null」と同様の動作をする
echo 2017/{12..01}/{31..01}$'\n' | date -f - "+%F %m %w" 2>&-

# awkを使って、3列目(曜日)が5のもののみを抽出する
echo 2017/{12..01}/{31..01}$'\n' | date -f - "+%F %m %w" 2>&- | awk '$3~ /5/'

# さらにawkに条件を追加して、2列目と3列目が同じものを以後表示させない
echo 2017/{12..01}/{31..01}$'\n' | date -f - "+%F %m %w" 2>&- | awk '$3~ /5/ && !a[$2$3]++'

# printで1列目のみを表示させるようにする
echo 2017/{12..01}/{31..01}$'\n' | date -f - "+%F %m %w" 2>&- | awk '$3~ /5/ && !a[$2$3]++{print $1}'

# 降順で日付を出力させていたので、tacで昇順にする
echo 2017/{12..01}/{31..01}$'\n' | date -f - "+%F %m %w" 2>&- | awk '$3~ /5/ && !a[$2$3]++{print $1}' | tac

実行結果。

[root@BS-PUB-CENT7-01 ~]# echo 2017/{12..01}/{31..01}$'\n' | date -f - "+%F %m %w" 2>&- | awk '$3~ /5/ && !a[$2$3]++{print $1}' | tac
2017-01-27
2017-02-24
2017-03-31
2017-04-28
2017-05-26
2017-06-30
2017-07-28
2017-08-25
2017-09-29
2017-10-27
2017-11-24
2017-12-29

その他の解き方としては、@Heliac1999さんのweek数とかこっちのやり方、@ebanさんのこれとかこれがとても参考になる。 …プレミアムフライデー、効果はどうかわからないけど、一般的になれば早く帰れるのでありがたいですね。