Edited at

(今は亡き)java.util.Calendarで誰も踏まなそうな地雷を踏んだ


おことわり

ネタです。普通に使っていればハマらない地雷を踏んでしまったので共有します。


結論

駄文なので先に結論だけ書いておきます。

変なソースコードを書く人がいるので、CalendarではなくDateTimeを使おう。


問題のソース

とある製品で見つけた「月曜日」に「2を足す」という謎のソースコードです。

仕様書がないのでどんな動作を期待してこのコードを書いたのかわかりませんが、動作検証をしてみました。

Calendar calendar = Calendar.getInstance();

calendar.add(Calendar.MONDAY, 2);
// これどんな結果になるの…


結果

なんということでしょう。2ヶ月後の日付になりました。

Calendar calendar = Calendar.getInstance();

System.out.println(calendar.getTime());
// Sat Oct 13 14:48:40 JST 2018

calendar.add(Calendar.MONDAY, 2);
System.out.println(calendar.getTime());
// Thu Dec 13 14:48:40 JST 2018


理由

Calendarの実装に理由が隠されていました。

Calendarクラスを継承しているGregorianCalendarクラスのaddメソッドを見てみます。

以下のようにaddメソッドではint型のfield変数によって、if文で処理を分けているのがわかります。

    @Override

public void add(int field, int amount) {
//略
if (field == YEAR) {
//略
} else if (field == MONTH) {
//略
}

ところで、Calendar.MONTHとかCalendar.MONDAYって数値だと何で表されているんでしょう。

    //略

public final static int MONTH = 2;
//略
public final static int MONDAY = 2;
//略

はい、どうもありがとうございました。月と月曜日で定数の値が同じなので、「月曜日に2を足す」というコードを書くと、「日付が2ヶ月進む」ということです。怖いぞ。


最後に

最後までネタにお付き合いいただきありがとうございました。

普通は踏まない地雷を踏む人がいるかもしれないので、今から日付の操作を行う場合はDateTimeAPIや、jodaTimeライブラリなどを積極的に使いましょう。

以下、DateTimeAPIの使い方に関して見つけた記事です。

Java 8 DateTime API をじわじわと使っていきたい(今更)

java.util.Calendar を Date and Time API に置き換える