期待の分析(疑似コード)
startDate.plus(Period.between(startDate, endDate)) == endDate
いくつかのトピックについて話し合う必要があります。
- 月や日などの個別の単位をどのように処理しますか?
- 期間(または「期間」)の追加はどのように定義されますか?
- 2つの日付間の時間的な距離(期間)を決定する方法
- 期間(または「期間」)の減算はどのように定義されますか?
最初にユニットを見てみましょう。日は可能な限り最小のカレンダー単位であり、すべてのカレンダー日付は完全な日数で他の日付とは異なるため、問題ありません。したがって、正または負の場合は常に等しい疑似コードを使用します。
startDate.plus(ChronoUnit.Days.between(startDate, endDate)) == endDate
ただし、グレゴリオ暦は異なる長さの暦月を定義するため、月は扱いにくいです。そのため、日付に月の整数を追加すると、日付が無効になる可能性があります。
[2019-08-31] + P1M = [2019-09-31]
java.time
終了日を有効な日付(ここでは[2019-09-30])に減らすという決定は合理的であり、最終日でも計算された月が保持されるため、ほとんどのユーザーの期待に対応します。ただし、月末の修正を含むこの追加は元に戻せません。減算と呼ばれる元に戻された演算を参照してください。
[2019-09-30]-P1M = [2019-08-30]
a)月追加の基本的なルールは、月の日をできるだけ多く保つことであり、b)[2019-08-30] + P1M = [2019-09-30]であるため、結果も妥当です。
期間(期間)の正確な追加とは何ですか?
のjava.time
a Period
は、年、月、日と整数の部分的な金額で構成されるアイテムの構成です。したがって、a Period
の追加は、開始日に部分的な金額を追加することで解決できます。年は常に月の12の倍数に変換できるため、うるう年の奇妙な副作用を回避するために、最初に年と月を組み合わせてから、合計を1ステップで追加できます。日は最後のステップで追加できます。で行われたような合理的なデザインjava.time
。
Period
2つの日付の間の権利を判断する方法?
最初に、期間が正の場合、つまり開始日が終了日より前の場合について説明します。次に、最初は月単位で、次に日単位で差を決定することで、常に期間を定義できます。それ以外の場合、2つの日付の間の期間はすべて日のみで構成されるため、この順序は月コンポーネントを実現するために重要です。あなたの例の日付を使う:
[2019-09-30] + P1M1D = [2019-10-31]
技術的には、開始日は最初に、計算された開始と終了の間の月の差によって前方に移動されます。次に、移動された開始日と終了日の差としての日差が、移動された開始日に追加されます。このように、例では期間をP1M1Dとして計算できます。これまでのところ合理的です。
期間を引く方法は?
前の追加の例で最も興味深い点は、誤って月末の修正がないということです。それにもかかわらずjava.time
、逆減算を実行できません。最初に月を減算し、次に日を減算します。
[2019-10-31]-P1M1D = [2019-09-29]
java.time
代わりに、以前の追加のステップを逆にしようとした場合、自然な選択は、最初に日を減算し、次に月を減算することでした。この変更された注文により、[2019-09-30]が取得されます。減算の変更された順序は、対応する加算ステップで月末の修正がない限り、役立ちます。これは、開始日または終了日の月の日付が28(可能な最小の月の長さ)以下の場合に特に当てはまります。残念なことにjava.time
、減算の別の設計が定義されていますが、Period
その結果、一貫性のない結果になります。
減算で期間の加算を元に戻すことはできますか?
最初に、特定のカレンダー日付から期間を減算する際に提案される変更された順序は、加算の可逆性を保証しないことを理解する必要があります。追加で月末の修正があるカウンターの例:
[2011-03-31] + P3M1D = [2011-06-30] + P1D = [2011-07-01] (ok)
[2011-07-01] - P3M1D = [2011-06-30] - P3M = [2011-03-30] :-(
より一貫した結果が得られるため、順序を変更しても問題はありません。しかし、残りの欠陥をどうやって治療するのでしょうか?残された唯一の方法は、継続時間の計算も変更することです。P3M1Dを使用する代わりに、期間P2M31Dが両方向で機能することがわかります。
[2011-03-31] + P2M31D = [2011-05-31] + P31D = [2011-07-01] (ok)
[2011-07-01] - P2M31D = [2011-05-31] - P2M = [2011-03-31] (ok)
つまり、計算された期間の正規化を変更するという考えです。これは、計算された月のデルタの加算が減算ステップで可逆的であるかどうかを確認することで実行できます。つまり、月末の修正の必要性を回避できます。java.time
残念ながら、そのようなソリューションは提供されていません。これはバグではありませんが、設計上の制限と見なすことができます。
代替案?
上記のアイデアを展開する可逆メトリックにより、タイムライブラリTime4Jを強化しました。次の例を参照してください。
PlainDate d1 = PlainDate.of(2011, 3, 31);
PlainDate d2 = PlainDate.of(2011, 7, 1);
TimeMetric<CalendarUnit, Duration<CalendarUnit>> metric =
Duration.inYearsMonthsDays().reversible();
Duration<CalendarUnit> duration =
metric.between(d1, d2); // P2M31D
Duration<CalendarUnit> invDur =
metric.between(d2, d1); // -P2M31D
assertThat(d1.plus(duration), is(d2)); // first invariance
assertThat(invDur, is(duration.inverse())); // second invariance
assertThat(d2.minus(duration), is(d1)); // third invariance
date.plus(period).minus(period)
)、結果は必ずしも同じ日付ではないことを理解しています。この質問は、Period.between
関数の不変条件についての詳細です。