Преобразование time4j Duration<IsoUnit> из P365D в P1Y и т. Д.

Instant i1 = Instant.now();
Instant i2 = Instant.now().plusSeconds(60 * 60 * 24 * 365);
Duration<IsoUnit> dur = Duration.from((TemporalAmount) java.time.Duration.between(i1, i2));
System.out.println(dur);

этот код печатает P365D, есть ли способ заставить единицы заполнять следующую большую единицу, так что это становится P1Y, и если у меня было как P334D до P11M и так далее?

версия time4j - 4.38

2 ответа

Решение

В других комментариях и ответах совершенно верно сказать, что год не всегда равен 365 дням.

Time4J-метод net.time4j.Duration.from(TemporalAmount) возвращает нормализованную продолжительность, используя STD_PERIOD как нормализатор. Документация этого нормализатора гласит:

Нормализует элементы длительности на основе 1 года = 12 месяцев и 1 дня = 24 часа и 1 часа = 60 минут и 1 минуты = 60 секунд - без преобразования дней в месяцы.

Таким образом, вы можете ожидать, что результат будет в днях, когда вы начнете с временной суммы, определенной в секундах.

Если вам все еще нужен год, я советую сначала преобразовать ваши моменты в календарные даты, используя соответствующий часовой пояс. Но в високосные годы ваш код будет по-прежнему производить 365 дней, а не год после добавления 60 * 60 * 24 * 365 секунд до второго момента. Таким образом, ваше добавление секунд также является ошибочным, поскольку оно основано на ложных предположениях.

Примечание:

Если вы хотите обратный путь, а именно, сколько секунд в году, то вы можете использовать такой код

Moment m1 = Moment.nowInSystemTime();
Moment m2 = m1.toZonalTimestamp(ZonalOffset.UTC).plus(1, CalendarUnit.YEARS).atUTC();
long seconds = SI.SECONDS.between(m1, m2); // = 366 days in seconds if applied on date 2019-05-22!

В будущей версии Time4J и возможной дополнительной секунде в конце 2019 года код может даже создать дополнительную секунду.

В любом случае, я советую вам обновить Time4J до v5.4 и рассмотреть следующие сопоставления:

java.time.Instant as input => net.time4j.MachineTime.from(...)
java.time.LocalDateTime/net.time4j.PlainTimestamp => net.time4j.Duration.from(...)

Поэтому, если вы действительно хотите, чтобы годы выводились в виде длительностей печати, и у вас есть моменты / моменты, то сначала преобразуйте в LocalDateTime/PlainTimestamp (используя часовой пояс или смещение), прежде чем создавать соответствующий объект продолжительности.

Обновление с 2019-05-25:

Другой способ с версией v5.4 (или позже) возможен через "нечеткие" длительности. Вы можете нормализовать длительности, которые вы получаете, применяя приближение. Пример:

    Duration<IsoUnit> d = Duration.of(60 * 60 * 24 * 365, ClockUnit.SECONDS);
    d = d.with(Duration.approximateMaxUnitOnly());
    System.out.println(d); // P1Y

Из-за природы такого рода нормализации вы не можете ожидать точных результатов.

Год не имеет фиксированного количества секунд:

  1. У високосных лет есть дополнительный день 29 февраля
  2. Иногда добавляются високосные секунды

Из-за вышеизложенного вы должны знать, сколько секунд в году. Кажется, вместо Instant а также Duration вы должны использовать LocalDate а также Period (по крайней мере при использовании java.time):

LocalDate d1 = LocalDate.now();
LocalDate d2 = d1.plusYears(1);
Period p = Period.between(d1, d2);
System.out.println(p); // P1Y
Другие вопросы по тегам