Delphiで翌月を求める場合には、DateUtilsのIncMonthとStartOfAMonthを使うと確実という話。
DateTimePickerを2つフォームに置き、下記の要領で計算。だいぶ冗長に書いています。
procedure TForm1.Button1Click(Sender: TObject); var TempDate: TDate; y, m, d: Word; begin // テンポラリにコピー TempDate := DateTimePicker1.DateTime; // 月を足す IncMonth(TempDate); // 分解 DecodeDate(TempDate, y, m, d); // 年と月から初日を求める DateTimePicker2.DateTime := StartOfAMonth(y, m); end;
で、なぜ最初から分解したyとmを使わず、IncMonthしてからわざわざ分解するかというところですが、IncMonthが「年の繰り越し」をやってくれるからです。
例えば2021/12/15を分解すると、2021、12、15となります。これを単純にStartOfAMonth(y, m+1)として送ると、2021年13月として送ることになります。当然EConvertErrorを発生します。
また、1月になるということは年が変わり、2022になる必要があります。
分解した場合、これらの例外や年の繰り越しを自前で実装しなければなりません。
存在しない日付も正確に
IncMonthに2021/12/15を渡すと、しっかりと「2022/1/15」を返してくれます。
ここで疑問がわきます。
1月には31日、2月は28もしくは29日しかありません。
1/31に月だけ足し込むと「2/31」となり、あり得ない日付が発生します。その場合は余剰日付が繰り上がって3/2などになってしまうのでしょうか?
実験してみます。
2021/01/31をIncMonthするとどうなるでしょうか?
なんと「2021/02/28」を返してくれます!
うるう年の2020/01/31をIncMonthするとどうなるでしょうか?
「2020/02/29」を返してくれます!2/28にはなりません。
つまり、31日は2月には無いことを判定し、さらに月の最終日を確認して、余剰分は丸めてから返してくれるのです。意図せず翌々月が返ってくることもありません。
当然、3/31を渡すと4/30になります。
IncMonth優秀です。
こういったこまごまとした例外を避けるためにも、まずIncMonthで月を足すことで、安全で短いコードになります。
コメント