Dateクラスを色々と使ってみる

## 環境
Ruby2.6

## はじめに
Rubyで日付を扱う場合、使うクラスはTimeクラスか、Dateクラスとなります。システム開発の現場で、よく使いそうなDateクラスについて動作確認を行いました。

## 動作確認
Timeクラスを使う場合は、require "time"を定義する必要はありませんが、Dateクラスを使う場合は、reguire "date"を定義する必要があります。
```test.rb
require "date"
```

システム日付はTimeクラスでも取得できますが、Dateクラスからも取得できます。
```test.rb
puts Date.today                             # => 2022-10-24
```

確認すると、確かにDate型になっています。
```test.rb
puts Date.today.class                       # => Date
```

Dateクラスで取得したシステム日付を年月の形式（YYYY-MM）の文字列にする場合です。Timeクラスを使うときと使い方は同じです。
```test.rb
puts Date.today.year.to_s + "-" + Date.today.month.to_s # => 2022-10
puts Date.today.strftime("%Y-%m")                       # => 2022-10
```

Dateクラスを使って、文字列をDate型に変換する場合です。Timeクラスのlocalメソッドを使う場合と、引数の渡し方が異なります。月日の先頭を０埋めしてもしなくても、０が付いた形式で返してくれます。
```test.rb
puts Date.parse("2022-10-09")               # => 2022-10-09
puts Date.parse("2022-1-9")                 # => 2022-01-09
```

確認すると、確かにDate型になっています。
```test.rb
puts Date.parse("2022-1-9").class           # => Date
```

日付の区切文字はスラッシュでもOKです。
```test.rb
puts Date.parse("2022/10/9")                # => 2022-10-09
```

引数に日付の区切がなくてもOKです。しかし、月も日も先頭０埋めになっていて、yyyymmddの形式になっていることが前提になります。このような引数の渡し方は個人的に少し危険な気がするため、日付は区切って渡した方が無難だとは思います。
```test.rb
puts Date.parse("20221009")                 # => 2022-10-09
```

parseメソッド以外に、newメソッドを使って、Date型に変換する方法もあります。基本的にどっちを使ってもいいでしょう。
```test.rb
puts Date.new(2022, 10, 25)                 # => 2022-10-25
puts Date.new(2022, 10, 25).class           # => Date
```

newメソッドを使う場合、引数に文字列を指定することはできません。
```test.rb
puts Date.new("2022", "10", "25")           # =>  undefined method `div' for "25":String
```

引数に日を省略して年月だけを指定することもできます。
```test.rb
puts Date.new(2022, 10)                     # =>  2022-10-01
```

Dateクラスには日付の論理チェックをしてくれるメソッドが用意されています。
```test.rb
puts Date.valid_date?(2022, 10, 24)         # => true
puts Date.valid_date?(2022, 9, 31)          # => false
puts Date.valid_date?(2022, 9, 0)           # => false
```

引数に使用できるデータ型は整数型だけです。文字列を渡すことはできません。
```test.rb
puts Date.valid_date?("2022", "9", "1")     # => no implicit conversion of String into Integer
```

これもエラーになります。個人的には、エラーを出すのではなく、falseで返してくれると便利なんだけど・・・と思ってしまいますが。
```test.rb
puts Date.valid_date?(2022, 9, bb)          # => undefined local variable or method `bb' for main:Object
```

これもエラーになります。割と要注意な気がします。この辺の挙動は知っておいた方がいいでしょう。
```test.rb
puts Date.valid_date?(2022, nil, 10)        # => no implicit conversion from nil to integer
puts Date.valid_date?(2022, "", 10)         # => no implicit conversion of String into Integer
```

これがtureで返るのは要注意な気がします。しかし、ここでバグることはあまり考えられなさそうではありますが。
```test.rb
puts Date.valid_date?(2022, -1, 10)         # => true
```

文字列型の日付に対して、日付の論理チェックを行う場合は、年、月、日に分割した後、整数型に変換して、valid_dateメソッドに引き渡す必要があります。
```test.rb
_date = "2022/10/01"
puts Date.valid_date?(_date.split("/")[0].to_i, _date.split("/")[1].to_i, _date.split("/")[2].to_i)   # => true
```

月末を求める場合です。第３引数に-1を指定すれば月末日が取得できます。
```test.rb
puts Date.new(2022, 2, -1)                  # => 2022-02-28
puts Date.new(2023, 1, -1)                  # => 2023-01-31
puts Date.new(2023, 2, -1)                  # => 2023-02-28
```

第３引数に-2を指定すれば、月末の前日を取得します。こういう使い方をする場面は個人的にはあまりなさそうな気はしますが。
```test.rb
puts Date.new(2023, 2, -2)                  # => 2023-02-28
```

月末の日付だけを取得する場合です。
```test.rb
puts Date.new(2023, 2, -1).to_s.split("-")[2]  # => 28
```


何日後、何日前を求める場合です。引数に０を指定すると、指定した日付が返ります。また、引数にマイナス数字を指定することもできます。
```test.rb
puts Date.parse("2022/10/25").prev_day(5)       # =>2022-10-20
puts Date.parse("2022/10/25").prev_day(0)       # =>2022-10-25

puts Date.parse("2022/10/25").next_day(5)       # =>2022-10-30
puts Date.parse("2022/10/25").next_day(0)       # =>2022-10-25
```

これは同じ結果になります。
```test.rb
puts Date.parse("2022/10/25").prev_day(-3)      # =>2022-10-28
puts Date.parse("2022/10/25").next_day(3)       # =>2022-10-28
```

何か月後、何か月先を求める場合です。使い方は、prev_day、next_dayと同じです。
```test.rb
puts Date.parse("2022/10/25").prev_month(5)     # =>2022-05-25
puts Date.parse("2022/10/25").prev_month(0)     # =>2022-10-25
puts Date.parse("2022/10/25").prev_month(-1)    # =>2022-11-25
```

prev_month、next_monthを使う場合は、引数の日を省略してもOKです。
```test.rb
puts Date.parse("2022/10").prev_month(1)        # =>2022-09-01
```

経過日数を求める場合です。マイナスの日数も取得してくれます。
```test.rb
strymd = Date.new(2022, 10 ,25)
endymd = Date.new(2022, 10 ,26)
puts (endymd - strymd).to_i                 　　# => 1
```

```test.rb
strymd = Date.new(2022, 10 ,25)
endymd = Date.new(2022, 10 ,25)
puts (endymd - strymd).to_i                 　　# => 0
```

```test.rb
strymd = Date.new(2022, 10 ,26)
endymd = Date.new(2022, 10 ,25)
puts (endymd - strymd).to_i                 　　# => -1
```

```test.rb
strymd = Date.new(2022, 10 ,25)
endymd = Date.new(2023, 11 ,25)
puts (endymd - strymd).to_i                 # => 396
```