3/31の1ヶ月後が4/30になったり5/1になったりするメモ。
日付計算で月を加算 (減算) して、加算結果の日付が無効になったときの動きのメモ。
書くこと
- 加算結果が無効な日付になる例
- 各言語での例
- 仕様
- まとめ
加算結果が無効な日付になる例
日付計算で月を加算 (減算) したとき、加算結果の日付が無効になることがある *1
たとえば、3/31に1ヶ月を足すと4/31という無効な日付になるが、プログラミング言語により無効な日付の扱いが変わってくる。
いくつかの言語での例を書いてみる。
各言語での例
# Java $ jshell jshell> import java.time.LocalDate; jshell> LocalDate.of(2018, 3, 31).plusMonths(1); $2 ==> 2018-04-30 # Ruby $ irb irb(main):001:0> require "date" => true irb(main):002:0> Date.new(2018, 3, 31).next_month => #<Date: 2018-04-30 ((2458239j,0s,0n),+0s,2299161j)> # Golang $ gore -autoimport gore version 0.3.0 :help for help gore> time.Date(2018, 3, 31, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) 2018-05-01 00:00:00 Local
仕様
各言語の仕様を見てみる。
Java
This method adds the specified amount to the months field in three steps:
1. Add the input months to the month-of-year field
2. Check if the resulting date would be invalid
3. Adjust the day-of-month to the last valid day if necessary
For example, 2007-03-31 plus one month would result in the invalid date 2007-04-31. Instead of returning an invalid result, the last valid day of the month, 2007-04-30, is selected instead.
加算結果の日付が無効になるときは、月の日を有効な最後の日に調整するようだ。
Go
AddDate normalizes its result in the same way that Date does, so, for example, adding one month to October 31 yields December 1, the normalized form for November 31.
AddDateは、Dateと同じ方法で結果を正規化します。たとえば、10月31日に1か月を追加すると、11月31日の正規化された形式である12月1日が得られます。
まとめ
個人的には、Go のように3/31の1ヶ月後が5/1になるというのは変な感じがするが、4/31という無効な日付は4/30の1日後であるから、4/30の有効な1日後 = 5/1が返されるというのは納得できる。
むしろ、言語やライブラリの場合は仕様にはっきりと書かれているので使う側が気をつければいいが、お客さんや同僚と話しているときにこのあたりの "自然な加算結果" が違っていると、少し苦労しそう。
*1:うるう年が絡むと年の加算などでも同じことが起こる