5

java.util.Calendar の奇妙な動作に直面しました。

import static org.junit.Assert.*;
import org.junit.Test;

import java.util.Calendar;

public class Tester1 {
    @Test
    public void test_monthOfDate() {
        assertEquals(1, monthOfDate(2013, 1, 30)); // OK
        assertEquals(1, monthOfDate(2013, 1, 31)); // OK

        // Start of February
        assertEquals(2, monthOfDate(2013, 2, 1));  // FAIL
        assertEquals(2, monthOfDate(2013, 2, 28)); // FAIL
        // to the end of it

        // and after that it is okay also
        assertEquals(3, monthOfDate(2013, 3, 1));  // OK
    }

    public int monthOfDate(int year, int month, int day) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1);

        // just a simple get! but seems it is very important
        cal.get(Calendar.MONTH);
        //

        cal.set(Calendar.DAY_OF_MONTH, day);

        return cal.get(Calendar.MONTH) + 1;
    }
}

なぜこれが正確に起こっているのか知りたいですか?

4

1 に答える 1

18

問題は、2013 年 1 月 30 日のカレンダーから始めていることです。

次に、年を 2013 に設定していますが、これは問題ではありません。

次に、月を 1 (つまり 2 月) に設定します。ここで何が起こると思いますか? 実際には、月を 1 に設定する必要があることを記憶しますが、実際の時間値は再計算しません。時間の値getドキュメント(強調鉱山)に従って、呼び出し時に再計算されます。

set(f, value) は、カレンダー フィールド f を value に変更します。さらに、カレンダー フィールド f が変更されたことを示す内部メンバー変数を設定します。カレンダー フィールド f はすぐに変更されますが、ミリ秒単位のカレンダーの時間値は、get()、getTime()、getTimeInMillis()、add()、または roll() への次の呼び出しが行われるまで再計算されません。したがって、set() を複数回呼び出しても、不要な計算が複数回トリガーされることはありません。set() を使用してカレンダー フィールドを変更した結果、カレンダー フィールド、カレンダー フィールドの値、およびカレンダー システムによっては、他のカレンダー フィールドも変更される場合があります。さらに、get(f) は、カレンダー フィールドが再計算された後、set メソッドの呼び出しによって設定された値を必ずしも返すとは限りません。詳細は、具体的なカレンダー クラスによって決まります。

「1 月 30 日」を「2 月 30 日」に変更して計算を強制しようとすると、実際には私のボックスでは 3 月 2 日になってしまいますが、実装によっては異なる場合があります。

最良の修正は次のとおりです。

于 2013-01-30T13:43:26.093 に答える