3

以下のコードを見てください。

Calendar today1 = Calendar.getInstance();
today1.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
System.out.println(today1.getTime());

Calendar today2 = new GregorianCalendar(2010, Calendar.JULY, 14);
today2.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
System.out.println(today2.getTime());

私はかなり混乱しています...2010年7月14日として今日実行していると仮定すると、出力は次のようになります。

Fri Jul 16 14:23:23 PDT 2010
Wed Jul 14 00:00:00 PDT 2010

最も厄介なのは、today2.getTimeInMillis()(または他のget()メソッド)を追加すると、一貫した結果が得られることです。以下のコードの場合:

Calendar today2 = new GregorianCalendar(2010, Calendar.JULY, 14);
today2.getTimeInMillis();
today2.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
System.out.println(today2.getTime());

結果は次のとおりです。

Fri Jul 16 00:00:00 PDT 2010
4

3 に答える 3

4

答えは実際にはJavaDocに文書化されていますjava.util.Calendar

ここで引用:

set(f、value)は、カレンダーフィールドfをvalueに変更します。さらに、カレンダーフィールドfが変更されたことを示す内部メンバー変数を設定します。フィールドfはすぐに変更されますが、カレンダーのミリ秒は、get()、getTime()、またはgetTimeInMillis()の次の呼び出しが行われるまで再計算されません。

これであなたが見ている振る舞いは説明できますが、日付コーディングをたくさん行う場合はJodaTimeを検討する必要があるというあなたの質問に対する別の回答者に同意します。

于 2010-07-14T22:00:34.220 に答える
2

Calendar#getInstance()実際には、ではなくインスタンスを取得するために使用する必要がありますnew GregorianCalendar()。その行を次のように置き換えます

Calendar today2 = Calendar.getInstance();
today2.set(2010, Calendar.JULY, 14);

そしてそれはうまくいくでしょう。

申し訳ありませんが、動作の詳細な説明はありません。現在のJavaSEAPIの主要な壮大な失敗の1つであるCalendarと予想されます。java.util.Date集中的な日付/時刻操作を行っている場合は、JodaTimeを確認することをお勧めします。今後の新しいJava7には、JodaTime( JSR-310 )に基づく改良された日付/時刻APIが付属しています。

于 2010-07-14T21:40:18.077 に答える
0

(編集して申し訳ありませんが、これをもう少し読みやすくしたかったのですが、最初に答えを書いたときに正しく理解できませんでした...今はエッセイの長さですが、そこに行きます...)

すでに述べたことに加えて、問題は、返されたCalendarインスタンスが異なる方法で準備されていることから発生します。個人的にはこれは設計上の欠陥だと思いますが、それには十分な理由があるかもしれません。

を呼び出すと、デフォルトのコンストラクターを使用しCalendar.getInstance()て新しいコンストラクターが作成されます。GregorianCalendarこのコンストラクターはsetCurrentTimeMillis(time)、現在のシステム時刻で呼び出してから、保護されたメソッドを呼び出しますcomplete()

GregorianCalendarただし、作成したコンストラクターを使用して新しいものを作成すると、complete()が呼び出されることはありません。代わりに、とりわけ、set(field, value)提供されるさまざまな情報に対してのみ呼び出されます。これはすべてうまくいっていますが、いくつかの紛らわしい結果があります。

最初のケースでが呼び出されると、 dustmachinecomplete()がほのめかしているメンバー変数がチェックされ、再計算する必要のある情報が決定されます。これにより、すべてのフィールド(、、など)の計算を強制するブランチが生成されDAYますWEEK_OF_MONTHCalendarそれは確かに怠惰であることに注意してください。このインスタンス化の方法を使用すると、その場で明示的な再計算(またはこの場合は初期計算)が強制されることがあります。

では、これはどのような影響を及ぼしますか?2番目のオブジェクト作成の場合、事前のフィールド計算が実行されなかったことを考えると、2つのオブジェクトの状態は大きく異なります。1つ目はすべてのフィールド情報が入力され、2つ目は指定した情報のみが含まれます。さまざまなメソッドを呼び出す場合get*()、情報を取得するときに変更を加えると遅延再計算ステップが発生するため、問題はありません。ただし、この再計算が発生する順序により、2つの異なる初期状態の違いが明らかになります。

あなたの特定のケースでは、これは次の関連するコードが原因です。これは、次のコマンドcomputeTime()で要求したときに正しい時間を計算するために必ず呼び出されますgetTime()

boolean weekMonthSet = isSet[WEEK_OF_MONTH] || isSet[DAY_OF_WEEK_IN_MONTH];
...
boolean useDate = isSet[DATE];

if (useDate && (lastDateFieldSet == DAY_OF_WEEK
      || lastDateFieldSet == WEEK_OF_MONTH
      || lastDateFieldSet == DAY_OF_WEEK_IN_MONTH)) {
    useDate = !(isSet[DAY_OF_WEEK] && weekMonthSet);
}

最初のケースでは、その初期計算のためにすべてのフィールドが設定されます。これにより、trueが可能になり、 setの呼び出しで指定したものweekMonthSetとともに、falseになります。DAY_OF_WEEKset(field, value)useDate

set(field, value)ただし、2番目のケースでは、フィールドが計算されていないため、設定されるフィールドは、コンストラクターと後続の呼び出しで指定したフィールドのみです。したがって、コンストラクターごとにuseDatetrueであるため、trueのままになりますが、オブジェクト内の他のフィールドはどこにも計算されておらず、ユーザーによって設定されていないため、falseになります。isSet[DATE]weekMonthSet

trueの場合useDate、暗黙的に、日付情報を使用して時間の値を生成します。useDateがfalseの場合、情報を使用しDAY_OF_WEEKて予想時間を計算できるため、結果として差が生じます。

getTimeInMillis()最後に、これにより、呼び出す前に呼び出すgetTime()と予期しない動作が修正されるのはなぜかという疑問が生じます。結局のところ、両方のオブジェクトを呼び出した結果、フィールドが再計算されます。これは、(おそらく本物の)理由が何であれ、時間が計算されたset(field, value)に発生します。したがって、時間を1秒に1回計算するように強制すると、基本的に2つのオブジェクトの状態が整列します。その後、への呼び出しはすべて、両方のオブジェクトで一貫して機能するはずです。Calendarget*()

理想的には、2番目のケースで使用したコンストラクターは、一貫性の名の下にこの最初の計算ステップを実行する必要があります(ただし、パフォーマンス上の理由から、これは好ましくありません)が、そうではなく、これが得られます。

つまり、他の人が述べたように、JodaTimeはあなたの友達であり、明らかにこれらのクラスはそれほどではありません。:)

于 2010-07-14T22:38:04.160 に答える