Ruby、Railsでの日付・時間の操作系メソッドまとめ


## はじめに
アプリケーション開発において、日付や時間の情報というのは必ずといっていいほど絡んできます。
毎回日付・時間操作のメソッドを忘れてしまい、ググる時間が勿体ないので、まとめました。

今回だけでは網羅できていない部分とあると思うため、順次更新予定です。

### XX日前・後

#### Dateクラス
`-1`、`+1`をするだけです。

```ruby
require 'date'

today = Date.today
yesterday = today - 1
tomorrow = today + 1

p today
#<Date: 2021-04-28 ((2459333j,0s,0n),+0s,2299161j)>
p yesterday
#<Date: 2021-04-27 ((2459332j,0s,0n),+0s,2299161j)>
p tomorrow
#<Date: 2021-04-29 ((2459334j,0s,0n),+0s,2299161j)>
```

#### DateTimeクラス
Dateクラスと同様に`-1`、`+1`で対応出来ます。

```ruby
require 'date'

today = DateTime.now
yesterday = today - 1
tomorrow = today + 1

p today
#<DateTime: 2021-04-28T18:46:07+09:00 ((2459333j,35167s,22091000n),+32400s,2299161j)>

p yesterday
#<DateTime: 2021-04-27T18:46:07+09:00 ((2459332j,35167s,22091000n),+32400s,2299161j)>

p tomorrow
#<DateTime: 2021-04-29T19:00:30+09:00 ((2459334j,36030s,963561000n),+32400s,2299161j)>
```

#### Time
Timeクラスの場合`-1`、`+1`を指定すると単純に1秒の加算・減算になります。
Timeクラスで日付操作をする場面は少ないと思いますが、もし±1日を求めたい場合 `86400秒`で1日となリます。

```ruby
p now = Time.now 

# 2021-04-28 18:48:54.976575 +0900

p now -1
# 2021-04-28 18:48:53.976575 +0900

p now +1
# 2021-04-28 18:48:55.976575 +0900
```

```ruby
p now = Time.now 
# 2021-04-28 18:50:22.141586 +0900

p now -1 * 60 * 60 * 24
# 2021-04-27 18:50:22.141586 +0900
```

### TimeWithZone
主にRailsで使用されるクラス。あまり意識せずに使っている人も多いかもしれないが、Railsでアプリケーションを開発する場合、`TimeWithZone`を使っているケースが多いと思います。

`activesupport`を使えるようにgemをインストールします。

```terminal
gem install activesupport
```

```ruby
require 'active_support/all'

p Time.current
# 2021-04-28 19:27:49.093581 +0900

# 昨日
p TIme.current.yesterday
# 2021-04-27 19:27:49.093649 +0900

# 翌日
p Time.current.tomorrow
# 2021-04-29 19:27:49.093752 +0900

# 3日前
p Time.current.ago(3.days)
# 2021-04-25 19:27:49.093787 +0900

# 3日後
p Time.current.since(3.days)
# 2021-05-01 19:27:49.093837 +0900

# 1日前
current = Time.current
p current - 1.day
# 2021-04-27 19:30:54.945456 +0900
```
TimeWithZoneは日付関連のメソッドが豊富なため、色々な求め方が出来ます。

### Xヶ月

#### Dateクラス

```ruby
require 'date'

today = Date.today

p today
#<Date: 2021-04-28 ((2459333j,0s,0n),+0s,2299161j)>

# 1ヶ月前
p today << 1
#<Date: 2021-03-28 ((2459302j,0s,0n),+0s,2299161j)>

# 2ヶ月前
p today.prev_month(2)
#<Date: 2021-02-28 ((2459274j,0s,0n),+0s,2299161j)>

# 1ヶ月後
p today >> 1
#<Date: 2021-05-28 ((2459363j,0s,0n),+0s,2299161j)>

# 3ヶ月後
p today.next_month(3)
#<Date: 2021-07-28 ((2459424j,0s,0n),+0s,2299161j)>
```
`prev_month`、`next_month`は引数を指定しない場合それぞれ、レシーバの1ヶ月前、1ヶ月後を返します。

#### DateTimeクラス

`DateTime`は日付のときと同様に、`Date`クラスと同じメソッドで対応出来ます。

```ruby 
require 'date'

today = DateTime.now
p today
#<DateTime: 2021-04-28T19:39:15+09:00 ((2459333j,38355s,430222000n),+32400s,2299161j)>

# 1ヶ月前
p today << 1
#<DateTime: 2021-03-28T19:39:15+09:00 ((2459302j,38355s,430222000n),+32400s,2299161j)>

# 2ヶ月前
p today.prev_month(2)
#<DateTime: 2021-02-28T19:39:15+09:00 ((2459274j,38355s,430222000n),+32400s,2299161j)>

# 1ヶ月後
p today >> 1
#<DateTime: 2021-05-28T19:43:19+09:00 ((2459363j,38599s,727644000n),+32400s,2299161j)>

# 3ヶ月後
p today.next_month(3)
#<DateTime: 2021-07-28T19:43:19+09:00 ((2459424j,38599s,727644000n),+32400s,2299161j)>
```



#### TimeWithZone

```ruby
require 'active_support/all'

# 1ヶ月前
p Time.current.prev_month
# 2021-03-28 19:52:16.958094 +0900

# 1ヶ月後
p Time.current.next_month
# 2021-05-28 19:50:22.450258 +0900

# 3ヶ月前
p Time.current.ago(3.month)
# 2021-01-28 19:50:22.450291 +0900

# 3ヶ月後
p Time.current.since(3.month)
# 2021-07-28 19:50:22.450393 +0900

current = Time.current

# 1ヶ月前
p current -1.month
# 2021-03-28 19:50:22.450436 +0900

# 1ヶ月後
p current +1.month
# 2021-05-28 19:50:22.450436 +0900
```

### X時間・X分・X秒

#### DateTime

```ruby
require 'date'

today = DateTime.new(2021,4,29,0,0,0)

# 1時間後
p today + Rational(1,24)
#<DateTime: 2021-04-29T01:00:00+00:00 ((2459334j,3600s,0n),+0s,2299161j)>

# 1分後
p today + Rational(1,24*60)
#<DateTime: 2021-04-29T00:01:00+00:00 ((2459334j,60s,0n),+0s,2299161j)>

# 1秒後
p today + Rational(1,24*60*60)
#<DateTime: 2021-04-29T00:00:01+00:00 ((2459334j,1s,0n),+0s,2299161j)>

```

### 年月日や曜日など日時に関する情報を取得する

```ruby
require 'date'

today = Date.today

p today
#<Date: 2021-04-29 ((2459334j,0s,0n),+0s,2299161j)>

# 年
p today.year
#2021

# 月
p today.month
# 4

# 日
p today.day
# 29

# 曜日
week = ['日曜日','月曜日','火曜日','水曜日','木曜日','金曜日','土曜日' ]
p week[today.wday]
# "木曜日"
```

曜日を取得する`wday`メソッドに関しては、曜日の文字列が返り値になるのではなく、対応する数値が返って来ます。
(0-6、日曜日は0)
DateTimeクラスでも同様のメソッドで年月日と曜日を取得出来ます。


### 差分

#### Dateクラス
Date型の場合純粋に引き算をするだけで、日にちの差分が取得出来ます。
1点気をつける部分としては、Date型同士で引き算した際の戻り値が`Rational`型(分数などを扱えるクラス)で返って来るので、分子のみを取得する`numerator`メソッドを使用することで純粋な日付の差分のみを取得出来ます。

```ruby
require 'date'
d1 = Date.new(2021, 4, 30)
d2 = Date.new(2021, 5, 5)
p (d2 - d1).numerator
# 5
```

### フォーマット・パース

#### 日付 ⇛ 文字列
#### strftime
おそらく年月日操作で一番使用頻度が高いメソッド

```ruby
require 'date'

day = Date.new(2021, 5, 1)
p day.strftime('%Y年%m月%d日')
# "2021年05月01日"

# 0サプレス
p day.strftime('%Y年%-m月%-d日')
# "2021年5月1日"

p day.strftime('%Y/%m/%d')
# "2021/05/01"

# 0サプレス
p day.strftime('%Y/%-m/%-d')

# "2021/5/1"
```

#### 文字列 ⇛ 日付
#### strptime
```ruby
require 'date'
p Date.strptime("2021年4月30日", '%Y年%m月%d日')

```

#### parse
```ruby
require 'date'

p Date.parse('20210430')
#<Date: 2021-04-30 ((2459335j,0s,0n),+0s,2299161j)>

p Date.parse('2021/4/30')
#<Date: 2021-04-30 ((2459335j,0s,0n),+0s,2299161j)>

p Date.parse('2021-4-30')
#<Date: 2021-04-30 ((2459335j,0s,0n),+0s,2299161j)>
```