ビジネスロジックでの判定で、いわゆる「一ヶ月前」という実装を愚直に 1.month.ago (ago(1.month))
などとした場合。
「これは当日を含むのか」とか「一ヶ月って30日?」「例えば閏年で 1/30 から一ヶ月後って 2/29 と 3/1 どっち? 一ヶ月前になると逆パターンだけどどう?」みたいな質問をされて、「前者はそら当日含むやろ!」とは思ったんだけど、後者は「確かに一ヶ月の長さって固定じゃないもんな」と気付いて、真面目に調べるなどしてみた。
結論
日数ベースではなく、ちゃんと「暦」として解釈して一ヶ月後になる。
なので 2/29 の ago(1.month)
だと 1/31 になるし、1/31 のsince(1.month)
だと 2/28 (or 29) になる。
# 2024/1/31 の一ヶ月後 => 2024/2/29 [1] pry(main)> Time.parse('2024-1-31').since(1.month) 2024-02-29 00:00:00 +0900 # 2024/1/30 の一ヶ月後 => 2024/2/29 [2] pry(main)> Time.parse('2024-1-30').since(1.month) 2024-02-29 00:00:00 +0900 # 2024/1/29 の一ヶ月後 => 2024/2/29 [3] pry(main)> Time.parse('2024-1-29').since(1.month) 2024-02-29 00:00:00 +0900 # 2024/1/28 の一ヶ月後 => 2024/2/28 [4] pry(main)> Time.parse('2024-1-28').since(1.month) 2024-02-28 00:00:00 +0900 # 2024/2/29 の一ヶ月前 => 2024/1/29 [5] pry(main)> Time.parse('2024-2-29').ago(1.month) 2024-01-29 00:00:00 +0900 # 2024/2/28 の一ヶ月前 => 2024/1/28 [6] pry(main)> Time.parse('2024-2-28').ago(1.month) 2024-01-28 00:00:00 +0900 # 2024/3/31 の一ヶ月前 => 2024/2/29 [7] pry(main)> Time.parse('2024-3-31').ago(1.month) 2024-02-29 00:00:00 +0900
なぜそうなっているか
なるほどー、 ActiveRecord かしこいなあ!
という、何気なく保証される一ヶ月に対する学びのメモであった。