「//fix this/」の記事インデックス

//fix this/ Javaコードの間違いをさがせ!


このシリーズは、あなたのコーディング・スキルを試すコーナーです。毎号オラクルのJavaエバンジェリストたちが、Javaコードに関する難問を出題し、正解と解説を掲載します。是非、毎号チャレンジして貴方のコーディング能力の向上にお役立て下さい。


第15回目 (Java Magazine 2015/Mar-Apr からの出題)


1. 今回のテーマ 「日付と時間のAPI に関する問題」 

Java 8で導入された日付と時間の新APIにより、JDKの旧来の日時処理の機能が飛躍的に改善されています。JSR 310は、よりシンプルでエラーの発生しにくい新APIを採り入れただけでなく、複雑な日付ロジックの単体テストとタイムゾーンの処理を改善した新機能を導入しました。

Java 8の日付と時間のAPIの新たな特徴の1つはTemporalAdjustersの概念で、日付の調整を外部に切り出すために使用します。組込みのTemporalAdjustersは、その月(または年)の最終日を見つけるなど、多数の一般的な処理を行います。しかし、欧米の言い伝えで不吉な日とされる13日の金曜日が次に来るのはいつか、などのアプリケーション固有の処理を実行する場合はどうなるでしょうか。



2. コード

次のコードでは、まさに前述の処理を実行するTemporalAdjusterのラムダ式を定義しています。このコードは任意の入力日付(例:LocalDateまたはLocalDateTime)を受け取り、次に来る13日の金曜日を返します。


TemporalAdjuster friday13Adjuster = temporal -> {
temporal = temporal.with(ChronoField.DAY_OF_MONTH, 13);
while (temporal.get(ChronoField.DAY_OF_WEEK) !=
DayOfWeek.FRIDAY.getValue()) {
temporal = temporal.plus(1, ChronoUnit.MONTHS);
}
return temporal;
};
// Examples usage:
LocalDate today = LocalDate.now();
LocalDate nextFriday13 = today.adjustInto(friday13Adjuster); 

3.正しいのはどれ?

上記のTemporalAdjusterには誤りがあります。次のいずれだと思いますか?

1) 月の長さの違い。temporalをインクリメントするときに、それぞれの月に含まれる日数の違いを考慮に入れる必要がある。

2) タイムゾーンの問題。入力日付と一緒にタイムゾーン・コンポーネントを受け取って処理する必要がある。

3) 計算式のエラー。13日の金曜日を計算する式に根本的な問題がある。

4) 上記以外



<ヒント>
日付と時間のAPIに関するプレゼンテーションを再確認しましょう。






【答え】

3番です。
1)temporal.plus を使うことにより、月の長さの違いは、LocalDateインスタンスによって自動的に修正されます。

2)TemporalAdjusterに渡されるTemporalの基本実装は、タイムゾーンの補正部分を担当しています。(たとえば、ZonedDateTimeが使用された場合など)

3)これが問題ですね。与えられた月の日が、13日かそれよりも前であるか、または与えられた月の13日が金曜日でないならば、 計算式は問題なく機能します。もし、これらのケースが同時に重なる場合は、Temporal Adjusterは間違って過去の日付を返します。
これを修正するには、単純に、その月の日をチェックするロジックを追加し、ループを走らせる前に前もって月を増分すればよいのです。


if (temporal.get(
ChronoField.DAY_OF_MONTH) > 13)
temporal =
temporal.plus(1, ChronoUnit.MONTHS);

簡単な修正、そして、新しいDateとTimeのAPIのおかげで、日付フィールドが、0,1または1900なのかどうかといったことに気を揉まなくても、、アルゴリズムのデバッグに集中することができます。


<< 第14回へ戻る 

「//fix this/」の記事インデックス