Delphiで翌月1日を求めるならIncMonthを活用するべし!

Delphiで翌月を求める場合には、DateUtilsのIncMonthとStartOfAMonthを使うと確実という話。

DateTimePickerを2つフォームに置き、下記の要領で計算。だいぶ冗長に書いています。

Delphi IncMonth確認フォーム

 

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」を返してくれます!

Delphi IncMonth 1/31

 

うるう年の2020/01/31をIncMonthするとどうなるでしょうか?

「2020/02/29」を返してくれます!2/28にはなりません。

Delphi IncMonth 2020/1/31

 

つまり、31日は2月には無いことを判定し、さらに月の最終日を確認して、余剰分は丸めてから返してくれるのです。意図せず翌々月が返ってくることもありません。

当然、3/31を渡すと4/30になります。

IncMonth優秀です。

こういったこまごまとした例外を避けるためにも、まずIncMonthで月を足すことで、安全で短いコードになります。

コメント