简体   繁体   中英

java.time.Duration implementation vs ISO8601 standard

IIUC java.time should end multiple date libraries and related issues, and offer something good and standardized. So it was kinda shock to me to learn, that java.time.Duration violates iso8601 standard. While it should be possible to parse: P2Y1DT1S , as that is valid by standard, java.time.Duration will parse only up to days (so you have to workaround via P731DT1S and java.time.Period can't do that at all, since it does not allow temporal part.

Questions:

  • Why? Is there a valid reason not to have interval parsing according to specification? What am I overlooking?

  • Or is there some readily available class, which will parse ISO8601?

tl;dr

Use the org.threeten.extra.PeriodDuration class.

PeriodDuration
        .between(
                LocalDate
                        .of( 2023 , Month.JANUARY , 23 )
                        .atTime( LocalTime.NOON )
                        .atZone( ZoneId.of( "America/New_York" ) ) ,
                LocalDate
                        .of( 2023 , Month.JANUARY , 23 )
                        .atTime( LocalTime.NOON )
                        .atZone( ZoneId.of( "America/New_York" ) )
                        .plusDays( 2 )
                        .plusHours( 17 )
                        .plusMinutes( 30 )
        )
        .normalizedStandardDays()
        .toString()

P2DT17H30M

Details

The Answer by MC Emperor is correct and wise. Always think twice before making use of a time span combining years-months-days with hours-minutes-seconds.

PeriodDuration

But if you insist on that combination, there is a class for that. You will find it in the ThreeTen-Extra library. This library is led by the same man, Stephen Colebourne, who leads the java.time (JSR 310) framework and its predecessor, Joda-Time .

The org.threeten.extra.PeriodDuration class represents an amount of time in the ISO 8601 calendar system that combines a period and a duration.

Here is some example code.

ZoneId z = ZoneId.of( "America/New_York" );
LocalDate ld = LocalDate.of( 2023 , Month.JANUARY , 23 );
LocalTime lt = LocalTime.NOON;
ZonedDateTime start = ZonedDateTime.of( ld , lt , z );
ZonedDateTime end = start.plusDays( 2 ).plusHours( 17 ).plusMinutes( 30 );
PeriodDuration pd = PeriodDuration.between( start , end );

Dump to console.

System.out.println( start + "/" + end + " = " + pd );

When run:

2023-01-23T12:00-05:00[America/New_York]/2023-01-26T05:30-05:00[America/New_York] = P3DT-6H-30M

Notice in that result we have negative amounts for hours and minutes. This is because the two-phase logic:

  • First, examine dates, LocalDate objects. Use Period.between to count days where the beginning is inclusive while the ending is exclusive . From the 23rd to the 26th is 3 days.
  • Second, examine time-of-day values, LocalTime objects. Use Duration.between to calculate elapsed time. We are calculating a duration between noon and 5:30 AM, so we must go back in time six and a half hours.

Therefore our result is 3 days minus 6.5 hours.

You can see this logic in the open source code .

If you want to smooth out this result to flip the negative hours-minutes, add call to PeriodDuration#normalizedStandardDays() .

PeriodDuration pd = PeriodDuration.between( start , end ).normalizedStandardDays();

2023-01-23T12:00-05:00[America/New_York]/2023-01-26T05:30-05:00[America/New_York] = P2DT17H30M

P2DT17H30M = P3DT-6H-30M

We now get 2 days and 17.5 hours, P2DT17H30M. This is the same amount of time as 3 days minus 6.5 hours, P3DT-6H-30M.

Well, parsing such a string means it yields an object which supports both concepts to be present at the same time – a sort of PeriodDuration . But there is none in Java, and its rationale is explained here by JodaStephen:

Note that a Duration contains an amount of seconds, not separate amounts of seconds, minutes and hours. The amount of seconds can exceed 24 hours, thus a Duration can represent a "day". But it is a fixed 24 hours day. By contrast, the representation of a "day in Period is descriptive and takes into account DST. The state of a Period is formed from three separate fields - days, months and years.

Bear in mind that "The customer occupied the hotel room for 2 days and seventeen and a half hours, P2DT17H30M" has the possibility to be complicated by DST cutovers . Using Period and Duration separately things are clear - Period is affected by DST cutovers and Duration is not.

(Emphasis mine.)

So in short, the two concepts handle DST differently, and combining the two complicates things.


Regarding your second question, this answer here suggests ThreeTen Extra.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM