tl;dr
Javaの日付系処理においては、CalendarクラスよりLocalDateクラスを利用しましょう。
以上。
おはなし
これは私の勤め先で起こった本当にあった話です。
できごと
2019年5月31日金曜日、所属プロジェクトで定期実行しているCIが突然落ちた。
Java8で書いているソースコードの全テストをJenkinsを使って1日に4回動かしているのだが、つい昨日までオールグリーンだったのが突然エラーを吐くようになったのだ。
エラーログを見てみると、該当箇所はJava標準ライブラリのCalendarクラスの利用箇所。
使い方としては、現在月の当月日数を取得するためだ。
テストコードでは、2017年6月を現在日付としてモック化していたのだが、テスト対象コードは以下のようになっている。
(※あくまでサンプルで、実際のコードとは違います。)
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, LocalDate.now().getYear());
calenar.set(Calendar.MONTH, LocalDate.now().getMonth() - 1); // 月は0から始まるため、例えば1月であれば0をセットする
// 当月の日数を取得
int days = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
6月なので当然、30が返り値となる訳だが、デバッグしてみるとそれが31となっていた。
問題
数年前の開発当初から動かし続けてきたテストコードがいきなりレッドになってしまったので、ライブラリに何らかの修正が入ったのかと思い色々調べてみたわけでだが、
該当するような記事は特に見当たらず。。
Google検索で「Java calendar b」まで入力すると、「Java calendar バグ」がレコメンドされるくらいには何やら問題がありそう、、
とりわけそれっぽい記事は見つけられなかったのだが、公式にdeprecatedになっている訳ではないもののあまり推奨されている訳でもなく、
Java8で導入されたLocalDateを利用しましょうというような記事が見つかった程度。
解決策
LocalDate#lengthOfMonthを利用する。
そもそも一旦現在日付を取得する処理にてLocalDateを利用しているのだから、そのままLocalDateクラスのメソッドを利用すればいい話なのだが、数年前ではあまりJava8で導入されたLocalDataの利用が普及されていなかったのでしょう。
以下のようにすればいいだけ。
today = LocalDate.now();
// 当月の日数を取得
int days = today.lengthOfMonth();
これにてCIも無事回るようになった。安心して業務に取り組める訳。
おわり。