Period
Javaとの間の微妙な違いがよく分かりませんDuration
。
オラクルの説明、誕生日から何日経ったかは次のようにして調べることができるそうです(彼らが使用した例の日付を使用)。
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1960, Month.JANUARY, 1);
Period birthdayPeriod = Period.between(birthday, today);
int daysOld = birthdayPeriod.getDays();
しかし、彼らも指摘しているように、これはあなたが生まれたタイムゾーンと現在いるタイムゾーンを考慮していません。しかし、これはコンピューターなので、正確に計算できますよね? では、 を使用しますかDuration
?
ZoneId bornIn = ZoneId.of("America/New_York");
ZonedDateTime born = ZonedDateTime.of(1960, Month.JANUARY.getValue(), 1, 2, 34, 56, 0, bornIn);
ZonedDateTime now = ZonedDateTime.now();
Duration duration = Duration.between(born, now);
long daysPassed = duration.toDays();
実際の時刻は正確ですが、私が正しく理解していれば、夏時間などにより、日付が暦日を正しく表していない可能性があります。
では、自分のタイムゾーンに基づいて正確な答えを得るにはどうすればよいのでしょうか? 考えられる唯一の方法は、 の使用に戻ってLocalDate
、まず値からタイムゾーンを正規化しZonedDateTime
、次に を使用することですDuration
。
ZoneId bornIn = ZoneId.of("America/New_York");
ZonedDateTime born = ZonedDateTime.of(1960, Month.JANUARY.getValue(), 1, 2, 34, 56, 0, bornIn);
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime nowNormalized=now.withZoneSameInstant(born.getZone());
Period preciseBirthdayPeriod = Period.between(born.toLocalDate(), nowNormalized.toLocalDate());
int preciseDaysOld = preciseBirthdayPeriod.getDays();
しかし、正確な答えを得るのは非常に複雑に思えます。
ベストアンサー1
Java-8 クラスに関するあなたの分析はPeriod
多かれ少なかれDuration
正しいです。
- このクラスは
java.time.Period
カレンダーの日付精度に制限されます。 - このクラスは
java.time.Duration
秒 (およびナノ秒) の精度のみを処理しますが、日数は常に 24 時間 = 86400 秒と同等として扱います。
通常、人の年齢を計算する際には、時計の精度やタイムゾーンを無視するだけで十分である。パスポートなどの個人文書には、その人が生まれた正確な時刻が記録されていないためです。そうであれば、 -class はPeriod
その役割を果たします (ただし、そのメソッドはgetDays()
慎重に扱ってください - 以下を参照)。
しかし、より精度を高めたい場合は、タイムゾーンを考慮したローカル フィールドの観点から結果を記述します。そうですね、最初の部分 (精度) は によってサポートされていますDuration
が、2 番目の部分はサポートされていません。
Period
また、正確な時間差( では無視されます)が日数でのデルタに影響を与える可能性があるため、使用しても役に立ちませんPeriod
。さらに(コードの出力を印刷するだけです):
Period preciseBirthdayPeriod =
Period.between(born.toLocalDate(), nowNormalized.toLocalDate());
int preciseDaysOld = preciseBirthdayPeriod.getDays();
System.out.println(preciseDaysOld); // 13
System.out.println(preciseBirthdayPeriod); // P56Y11M13D
ご覧のとおり、この方法を使用しpreciseBirthdayPeriod.getDays()
て合計デルタを日数で取得するのは非常に危険です。いいえ、合計デルタの一部分にすぎません。11 か月と 56 年もあります。デルタを日数だけでなく印刷することも賢明だと思います。そうすれば、人々はデルタの大きさをより簡単に想像できるからです (ソーシャル メディアで「3 年、2 か月、4 日」のように期間が印刷されるよく見られる使用例を参照してください)。
明らかに、特別なタイムゾーン (この例では、誰かが生まれたタイムゾーン) で、カレンダー単位とクロック単位を含む期間を決定する方法が必要です。Java-8-time-library の悪い点は、Period
ANDの組み合わせをサポートしていないことですDuration
。また、外部ライブラリ Threeten-Extra-class をインポートしても、タイムゾーンの影響 (1 日 == 24 時間) が無視され、月などの他の単位でデルタを印刷することもできないため、Interval
役に立ちません。long daysPassed = interval.toDuration().toDays();
まとめ:
あなたは -ソリューションを試しましたPeriod
。@swiedsw による回答では、Duration
-ベースのソリューションを試しました。どちらのアプローチも、精度に関しては欠点があります。両方のクラスを組み合わせて、必要な時間演算を自分で実装して実現する新しいクラスを作成することもできますTemporalAmount
(それほど簡単ではありません)。
サイドノート:
私はすでに自分のタイムライブラリに実装していますタイム4Jあなたが探しているものなので、独自の実装のインスピレーションとして役立つかもしれません。例:
Timezone bornZone = Timezone.of(AMERICA.NEW_YORK);
Moment bornTime =
PlainTimestamp.of(1960, net.time4j.Month.JANUARY.getValue(), 1, 22, 34, 56).in(
bornZone
);
Moment currentTime = Moment.nowInSystemTime();
MomentInterval interval = MomentInterval.between(bornTime, currentTime);
MachineTime<TimeUnit> mt = interval.getSimpleDuration();
System.out.println(mt); // 1797324427.356000000s [POSIX]
net.time4j.Duration<?> duration =
interval.getNominalDuration(
bornZone, // relevant if the moments are crossing a DST-boundary
CalendarUnit.YEARS,
CalendarUnit.MONTHS,
CalendarUnit.DAYS,
ClockUnit.HOURS,
ClockUnit.MINUTES
);
// P56Y11M12DT12H52M (12 days if the birth-time-of-day is after current clock time)
// If only days were specified above then the output would be: P20801D
System.out.println(duration);
System.out.println(duration.getPartialAmount(CalendarUnit.DAYS)); // 12
この例は、月、日、時間などの単位を使用することは、厳密には正確ではないという私の一般的な考え方も示しています。(科学的な観点から) 厳密に正確な唯一のアプローチは、マシン時間を 10 進秒で使用することです (SI 秒が最適で、1972 年以降は Time4J でも使用可能です)。