Lokesh_K
Lokesh_K

Reputation: 563

Convert Instant to OffsetDateTime then Instant.parse() on the OffsetDateTime leads to DateTimeParseException for zero seconds cases

I am receiving instant from a service and below are the cases.

In first case, say instant = "2021-03-23T04:17:35Z" & In second case, instant = "2021-07-15T05:27:00Z"

Then required to convert instant to offsetDateTime, doing like

OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.UTC)

Now I want to calculate hours gap between above offsetDateTime and instant.now

ChronoUnit.HOURS.between(Instant.parse(offsetDateTime), Instant.now())

Results

case 1 : it works fine

case 2 : ERROR : DateTimeParseException: Text '2021-07-15T05:27Z' could not be parsed at index 16

Figured out the reason : in case 2, if it would have pass the same 2021-07-15T05:27:00Z. it would work but as instant.atOffset(ZoneOffset.UTC) internally will call to below method, where zero would be removed, basically minute part will be sorted out. so below fn will return 2021-07-15T05:27Z , this will lead to DateTimeParseException.

public static OffsetDateTime ofInstant(Instant instant, ZoneId zone) {
        Objects.requireNonNull(instant, "instant");
        Objects.requireNonNull(zone, "zone");
        ZoneRules rules = zone.getRules();
        ZoneOffset offset = rules.getOffset(instant);
        LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);
        return new OffsetDateTime(ldt, offset);
    }

One solution I am assuming manually append the zero but that's might not be good practice.

Upvotes: 6

Views: 20758

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 338785

The Answer by Avinash is correct.

In addition, let’s look at this code from the Question:

public static OffsetDateTime ofInstant(Instant instant, ZoneId zone) 
{
        Objects.requireNonNull(instant, "instant");
        Objects.requireNonNull(zone, "zone");
   
        ZoneRules rules = zone.getRules();
        ZoneOffset offset = rules.getOffset(instant);
        LocalDateTime ldt = LocalDateTime.ofEpochSecond(instant.getEpochSecond(), instant.getNano(), offset);

        return new OffsetDateTime(ldt, offset);
}

Firstly, if you want to apply an offset to a moment, there is no need for LocalDateTime class. Simply do this:

OffsetDateTime odt = instant.atOffset( myZoneOffset ) ;

See the tutorial on naming conventions for at…, from…, with…, etc.

When you want to perceive a moment thought the wall-clock time used by the people of a particular region, apply a time zone (ZoneId) to an Instant to get a ZonedDateTime. Use ZonedDateTime rather than OffsetDateTime. A ZonedDateTime is preferable to a mere OffsetDateTime because it contains more information. This information may be critical if adding or subtracting to move to another moment. This information is also useful when producing text to represent the content of this date-time object.

Understand that an offset from UTC is simply a number of hours-minutes-seconds. A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region as decided by politicians.

ZonedDateTime zdt = instant.atZone( myZoneId ) ;

So your method should be something like this.

public static ZonedDateTime ofInstant(Instant instant, ZoneId zone) 
{
    ZonedDateTime zdt = 
        Objects.requireNonNull( instant )
        .atZone(
            Objects.requireNonNull( zone )
        )
    ;
    return zdt ;
}

If you really need a OffsetDateTime, despite ZonedDateTime being preferred, extract one.

OffsetDateTime odt = zdt.toOffsetDateTime() ;

Upvotes: 6

Arvind Kumar Avinash
Arvind Kumar Avinash

Reputation: 79095

You do not need any DateTimeFormatter

You do not need any DateTimeFormatter to parse your Date-Time strings. The modern Date-Time API is based on ISO 8601 and does not require using a DateTimeFormatter object explicitly as long as the Date-Time string conforms to the ISO 8601 standards.

import java.time.Instant;
import java.time.temporal.ChronoUnit;

public class Main {
    public static void main(String[] args) {
        Instant instant1 = Instant.parse("2021-03-23T04:17:35Z");
        Instant instant2 = Instant.parse("2021-07-15T05:27:00Z");

        System.out.println(instant1);
        System.out.println(instant2);

        System.out.println(ChronoUnit.HOURS.between(instant1, instant2));
    }
}

Output:

2021-03-23T04:17:35Z
2021-07-15T05:27:00Z
2737

ONLINE DEMO

Learn more about the modern Date-Time API from Trail: Date Time.

Upvotes: 5

Related Questions