java.timeでCalendar.rollの同等とは何ですか?

スイーパー:

私は昔勉強したCalendarAPIは、それがいかに悪い見て、私はそれを発見Calendar持っているroll方法を。違っadd方法、rollより大きなカレンダーフィールドの値を変更しません。

例えば、カレンダ・インスタンスがc日付2019年8月31日を表します。呼び出しは、c.roll(Calendar.MONTH, 13)月フィールドに13が追加されますが、年を変更しないので、結果は2019年9月30日です。それは小さい分野であるため、月の日は、変化していることに注意してください。

関連

私は、現代では、このようなAの方法を見つけることを試みたjava.timeAPIを。私は、このような方法がでなければならないと思ったLocalDateLocalDateTime、私は一種の何も見つかりませんでした。

だから、私は自分の書き込みしようとしたroll方法を:

public static LocalDateTime roll(LocalDateTime ldt, TemporalField unit, long amount) {
    LocalDateTime newLdt = ldt.plus(amount, unit.getBaseUnit());
    return ldt.with(unit, newLdt.get(unit));
}

しかし、これは唯一のいくつかのケースではなく、他人のために動作します。例えば、それはで説明した場合には使用できません。ここでのドキュメント

(-1、Calendar.WEEK_OF_MONTH)もともと6月6日日曜日に設定されたGregorianCalendar、1999年のCallingロールを考えてみアドオンを呼び出すのに対し、火曜日1999年6月1日にカレンダーを設定します(Calendar.WEEK_OF_MONTHを、-1)日曜日5月30日にカレンダーを設定し、 WEEK_OF_MONTHがロールされたときにMONTHを変更してはならない:ロールルールは追加の制約を課しているため、1999年です。追加ルール1と一緒になって、結果として得られる日は、ルール2、DAY_OF_WEEK、WEEK_OF_MONTHを変更するときに、火曜日、日曜日にもっとも近い可能値(に設定されている不変を追加するためによると、火曜日6月1日(土)と6月5日の間でなければなりません日曜日は)週の最初の日です。

私のコード:

System.out.println(roll(
        LocalDate.of(1999, 6, 6).atStartOfDay(),
        ChronoField.ALIGNED_WEEK_OF_MONTH, -1
));

出力は1999-07-04T00:00、使用したのに対しCalendar

Calendar c = new GregorianCalendar(1999, 5, 6);
c.roll(Calendar.WEEK_OF_MONTH, -1);
System.out.println(c.getTime().toInstant());

出力1999-05-31T23:00:00Zで、1999-06-01私のタイムゾーンで。

同等何ですかrollにおけるjava.timeAPIは?ないものがある場合は、どのように私はそれを模倣するためにメソッドを書くことができますか?

オレVV:

まず、私は、任意の便利なアプリケーションを見た覚えていないことができますCalendar.roll第二に、私は機能は非常によくコーナーケースに指定されているとは思いません。そして、コーナーケースは興味深いものになります。13ヶ月で月をローリングすることなく、難しいことではないroll方法。これは、同様の観察は、この機能がjava.timeによって提供されていない理由があることかもしれません。

代わりに、私たちは、ローリングのより多くのマニュアルの方法に頼らなければならないだろうと信じています。あなたの最初の例を示します。

    LocalDate date = LocalDate.of(2019, Month.JULY, 22);
    int newMonthValue = 1 + (date.getMonthValue() - 1 + 13) % 12;
    date = date.with(ChronoField.MONTH_OF_YEAR, newMonthValue);
    System.out.println(date);

出力:

2019年8月22日

私は、ISOの年表で年に12ヶ月が常に存在するという事実を使用しています。以来%、常に0ベースの結果を与える、私は剰余演算の前に1ベースの月の値から1を減算し、その後に戻ってそれを追加し、私はポジティブロールを想定しています。ロールへの月数が負になる可能性がある場合は、それは少し複雑(読者に左)を取得します。

最小と大きなフィールド指定されたフィールドの可能な最大値を検索し、いくつかの剰余演算を実行します。他のフィールドのために私は似たようなアプローチは、ほとんどの場合のために働くだろうと思います。

これは、いくつかのケースでの挑戦になることがあります。その日はあなたが午前6時から37時間をロールバックする方法を、25時間の長さであるので、例えば、時にサマータイム(DST)が終了し、クロックは、午前2時まで3から後方になっていますか?私はそれを行うことができます確信しています。そして、私はまた、機能が内蔵されていないことを確信しています。

月の週を転がすと、あなたたとえば、古いものと現代的なAPIの間に別の違いが場に出る:GregorianCalendarカレンダーの日付と時間を定義するだけでなく、週の最初の日からなる週のスキームと最小値を定義します最初の週の日数。java.timeで週スキームは以下のように定義されるWeekFields代わりに、オブジェクト。月の週があいまいであってもよく転がりながらだから、GregorianCalendarそれはしていない週スキーム知らなくても、LocalDateまたはをLocalDateTime試みは、ISOの週(それに新しい月の少なくとも4日間を持っていることである月曜日に開始し、最初の週)と仮定するかもしれませんが、それは常に、ユーザーが意図していたものではないかもしれません。

年の月と週の週は、週クロス月と年の境界以来、特別です。ここでは、月の週のロールを実装する私の試みです:

private static LocalDate rollWeekOfMonth(LocalDate date, int amount, WeekFields wf) {
    LocalDate firstOfMonth = date.withDayOfMonth(1);
    int firstWeekOfMonth = firstOfMonth.get(wf.weekOfMonth());
    LocalDate lastOfMonth = date.with(TemporalAdjusters.lastDayOfMonth());
    int lastWeekOfMonth = lastOfMonth.get(wf.weekOfMonth());
    int weekCount = lastWeekOfMonth - firstWeekOfMonth + 1;
    int newWeekOfMonth = firstWeekOfMonth
            + (date.get(wf.weekOfMonth()) - firstWeekOfMonth
                            + amount % weekCount + weekCount)
                    % weekCount;
    LocalDate result = date.with(wf.weekOfMonth(), newWeekOfMonth);
    if (result.isBefore(firstOfMonth)) {
        result = firstOfMonth;
    } else if (result.isAfter(lastOfMonth)) {
        result = lastOfMonth;
    }
    return result;
}

やってみよう:

    System.out.println(rollWeekOfMonth(LocalDate.of(1999, Month.JUNE, 6), -1, WeekFields.SUNDAY_START));
    System.out.println(rollWeekOfMonth(LocalDate.of(1999, Month.JUNE, 6), -1, WeekFields.ISO));

出力:

1999-06-01
1999-06-30

説明:あなたは引用符は日曜日が週の最初の日である(「日曜日が週の最初の日であるところ、」それが終了し、それはおそらく米国で書かれた)ことを前提としてドキュメントので、週は日曜日6月6日の前があり、圧延によって-1週間は、この前の週にロールバックする必要があります。コードの私の最初の行は、それを行います。

日曜日6月6日月曜日から5月31日からの週に属しISO週スキーム、日曜日6月6日には、その月には週今週の前には存在しません。したがって、私たちは月の外に行くことができないので、7月4日を通して月、6月28日の最後の週にコードロールの私の二行目、6月30日が選ばれています。

私はそれは同じように動作するかどうかをテストしていませんGregorianCalendar比較のため、GregorianCalendar.roll実装が処理するために、52個のコード行を使用してWEEK_OF_MONTH私の20株と比較した場合に、。どちらか私は左の対価のうち、何か、またはjava.time再びショーも優位性を持っています。

むしろ、現実の世界のための私の提案は次のとおりです。古いAPIは行儀どのように無視して、あなたの要件がjava.timeの上に直接それらをクリアし、実施します。学術的な練習として、あなたの質問には、楽しく、興味深いものです。

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=230505&siteId=1