[bash]dateコマンド便利すぎる(Format指定,日時計算)

# 背景
Bash,ShellScript,ansible-playbookで日時をこねくり回したい時に、色々な方法があると思います。今回、`date`コマンドのオプションで事足りることが多かったので、用途ごとにここに記しておきます。

# 利用したVersion

```
$ date --version
date (GNU coreutils) 8.30
```


# 1. フォーマット指定(`+format`)
`+format`でフォーマットを指定できます。
`date`単体だと扱いにくいですが、フォーマット指定することで用途が広がります。
以下例にあるように、バックアップでよく使います。
```
$ date
Fri Dec 22 12:18:11 JST 2023

$ date +%Y%m%d
20231222

$ cp test.txt test.txt_`date +%Y%m%d` -v
'test.txt' -> 'test.txt_20231222'
```

## その他フォーマットオプション
`man date`から取得できるので。好みに合わせて選びます！
(man dateの結果を | 標準出力して | 空行削除して | Formatの箇所だけgrepしています)
```
$ man date|col -b|grep -v '^$'|grep -A 54 " FORMAT controls the output"
       FORMAT controls the output.  Interpreted sequences are:
       %%     a literal %
       %a     locale's abbreviated weekday name (e.g., Sun)
       %A     locale's full weekday name (e.g., Sunday)
       %b     locale's abbreviated month name (e.g., Jan)
       %B     locale's full month name (e.g., January)
       %c     locale's date and time (e.g., Thu Mar  3 23:05:25 2005)
       %C     century; like %Y, except omit last two digits (e.g., 20)
       %d     day of month (e.g., 01)
       %D     date; same as %m/%d/%y
       %e     day of month, space padded; same as %_d
       %F     full date; same as %Y-%m-%d
       %g     last two digits of year of ISO week number (see %G)
       %G     year of ISO week number (see %V); normally useful only with %V
       %h     same as %b
       %H     hour (00..23)
       %I     hour (01..12)
       %j     day of year (001..366)
       %k     hour, space padded ( 0..23); same as %_H
       %l     hour, space padded ( 1..12); same as %_I
       %m     month (01..12)
       %M     minute (00..59)
       %n     a newline
       %N     nanoseconds (000000000..999999999)
       %p     locale's equivalent of either AM or PM; blank if not known
       %P     like %p, but lower case
       %q     quarter of year (1..4)
       %r     locale's 12-hour clock time (e.g., 11:11:04 PM)
       %R     24-hour hour and minute; same as %H:%M
       %s     seconds since 1970-01-01 00:00:00 UTC
       %S     second (00..60)
       %t     a tab
       %T     time; same as %H:%M:%S
       %u     day of week (1..7); 1 is Monday
       %U     week number of year, with Sunday as first day of week (00..53)
       %V     ISO week number, with Monday as first day of week (01..53)
       %w     day of week (0..6); 0 is Sunday
       %W     week number of year, with Monday as first day of week (00..53)
       %x     locale's date representation (e.g., 12/31/99)
       %X     locale's time representation (e.g., 23:13:48)
       %y     last two digits of year (00..99)
       %Y     year
       %z     +hhmm numeric time zone (e.g., -0400)
       %:z    +hh:mm numeric time zone (e.g., -04:00)
       %::z   +hh:mm:ss numeric time zone (e.g., -04:00:00)
       %:::z  numeric time zone with : to necessary precision (e.g., -04, +05:30)
       %Z     alphabetic time zone abbreviation (e.g., EDT)
       By default, date pads numeric fields with zeroes.  The following optional flags may follow '%':
       -      (hyphen) do not pad the field
       _      (underscore) pad with spaces
       0      (zero) pad with zeros
       ^      use upper case if possible
       #      use opposite case if possible
       After  any  flags  comes an optional field width, as a decimal number; then an optional modifier, which is either E to use
       the locale's alternate representations if available, or O to use the locale's alternate numeric symbols if available.
```

# 2. 日付計算する(`--date`オプション)
`--date`オプションを利用することで、〇時間後、〇日前、差分計算ができる！

分かりやすいように`20231222`を起点にしていますが、省力可能です。（省略した場合は、dateコマンドの結果から計算される）

### 過去（〇〇前）
```
$ date --date "20231222 5 hours ago"
Thu Dec 21 19:00:00 JST 2023
$ date --date "20231222 5 hours ago"
Thu Dec 21 19:00:00 JST 2023
$ date --date "20231222 5 days ago"
Sun Dec 17 00:00:00 JST 2023
$ date --date "20231222 5 week ago"
Fri Nov 17 00:00:00 JST 2023
$ date --date "20231222 5 month ago"
Sat Jul 22 00:00:00 JST 2023
$ date --date "20231222 5 year ago"
Sat Dec 22 00:00:00 JST 2018
```
### 未来（〇〇後）
```
$ date --date "20231222 5 hours"
Fri Dec 22 05:00:00 JST 2023
$ date --date "20231222 5 days"
Wed Dec 27 00:00:00 JST 2023
$ date --date "20231222 5 week"
Fri Jan 26 00:00:00 JST 2024
$ date --date "20231222 5 month"
Wed May 22 00:00:00 JST 2024
$ date --date "20231222 5 year"
Fri Dec 22 00:00:00 JST 2028
```

### 混合（〇日前の〇時間後）
```
$ date --date "20231222 last day"
Thu Dec 21 00:00:00 JST 2023

$ date --date "20231222 last day 12 hours"
Thu Dec 21 12:00:00 JST 2023
```

# (番外編)Ansibleで使う時
lookupプラグインを使って計算可能 
```
date_calc: "{{ lookup('pipe', 'date -d \"20231222 last day 6 hours\" +\"%Y/%m/%d\"') }}"
```

# 参考記事
* [dateコマンドで日時(日付、時刻)計算をする方法](https://l-w-i.net/t/shell/date_001.txt)
* [(Linux)date -fオプションで時をかける](https://life-is-command.com/date-f/)
* [【Ansible】 Lookup プラグインで日付計算がしたい](https://qiita.com/masa2223/items/2cef65fce4687e7f1cf4)