1
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);

this code prints P365D, is there a way to make units fill up the next bigger unit, so this becomes P1Y and if i had like P334D to P11M and so on?

time4j version is 4.38

Meno Hochschild
  • 38,305
  • 7
  • 88
  • 115

2 Answers2

2

The other comments and answers are completely right to say that a year is not always equal to 365 days.

The Time4J-method net.time4j.Duration.from(TemporalAmount) returns a normalized duration using STD_PERIOD as normalizer. The documentation of this normalizer says:

Normalizes the duration items on the base of 1 year = 12 months and 1 day = 24 hours and 1 hour = 60 minutes and 1 minute = 60 seconds - without converting days to months.

So you can only expect the result to be in days when you start with a temporal amount defined in seconds.

If you still want a year then I suggest you to first convert your instants to calendar dates using an appropriate time zone. But in leap years, your code would still produce 365 days and not a year after having added 60 * 60 * 24 * 365 seconds to the second instant. So your addition of seconds is also flawed because it is based on false assumptions.

Side note:

If you want the reverse way, namely how many seconds are in a year then you might use code like

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!

With a future version of Time4J and possible leap second at the end of year 2019, the code might even produce an extra second.

Anyway, I advise you to update Time4J to v5.4 and consider following mappings:

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

So if you really want years as possible output in printing durations and you have instants/moments then first convert to LocalDateTime/PlainTimestamp (using a time zone or offset) before you create the appropriate duration object.

Update from 2019-05-25:

Another way with the version v5.4 (or later) is possible via "fuzzy" durations. You could normalize the durations you get by applying an approximation. Example:

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

Due to the nature of this kind of normalization, you cannot expect exact results.

Meno Hochschild
  • 38,305
  • 7
  • 88
  • 115
1

A year doesn't have a fixed amount of seconds:

  1. Leap years have an additional day on February 29
  2. Leap seconds are sometimes added

Due to above you have to know how many seconds are in a year. It seems like instead of Instant and Duration you should be using LocalDate and Period (at least when using java.time):

LocalDate d1 = LocalDate.now();
LocalDate d2 = d1.plusYears(1);
Period p = Period.between(d1, d2);
System.out.println(p); // P1Y
Karol Dowbecki
  • 38,744
  • 9
  • 58
  • 89