Zack
Zack

Reputation: 2208

DateTimeParseException while parsing date using ZonedDateTime

I have the below program and looks like ZonedDateTime is not able to parse the date string. Should I use a different date format or different library to parse?

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

class Scratch {
    public static void main(String[] args) {
        final String inputDate = "2022-03-12T03:59:59+0000Z";
        ZonedDateTime.parse(inputDate, DateTimeFormatter.ISO_DATE_TIME).toEpochSecond();
    }
}

Exception in thread "main" java.time.format.DateTimeParseException: Text '2022-03-12T03:59:59+0000Z' could not be parsed, unparsed text found at index 19
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2053)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
    at java.base/java.time.ZonedDateTime.parse(ZonedDateTime.java:599)
    at Scratch.main(scratch_29.java:7)

Process finished with exit code 1

Upvotes: 2

Views: 1129

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 340070

tl;dr

Your input happens to be a screwy variation of the standard IS0 8601 format used by default with the java.time.Instant class. Fix your input string, and parse.

Instant.parse( "2022-03-12T03:59:59+0000Z".replace( "+0000Z" , "Z" ) )

Or, reformatted:

Instant.parse
( 
    "2022-03-12T03:59:59+0000Z"
    .replace( "+0000Z" , "Z" ) 
)

See this code run live at IdeOne.com.

2022-03-12T03:59:59Z

But the very best solution is to use only ISO 8601 formats when exchanging date-time values.

Details

ZonedDateTime inappropriate here

ZonedDateTime is the wrong class for your input. Your input has a Z which is a standard abbreviation for an offset from UTC of zero hours-minutes-seconds. But no time zone indicated, only a mere offset. So use OffsetDateTime or Instant rather than ZonedDateTime.

+0000 and Z redundant

The +0000 also means an offset of zero hours-minutes-seconds. This is the same meaning as the Z. So this part is redundant. I suggest you educate the publisher of your data about the standard ISO 8601 formats. No need to invent formats such as seen in your input.

String manipulation rather than custom formatter

If all your inputs have the same ending of +0000Z, I suggest you clean the incoming data rather than define a formatting pattern.

String input = "2022-03-12T03:59:59+0000Z".replace( "+0000Z" , "Z" ) ;
Instant instant = Instant.parse( input ) ;

Other formats

You later commented:

"2017-01-04T12:30:00-05:00" , "2017-03-20T22:05:00Z", etc. So I cannot assume all my inputs to have the same ending of +0000Z.

Both of those are standard ISO 8601 formats. Both can be parsed as OffsetDateTime objects as-is, without alteration.

So I still maintain that the simplest approach, given your range of possible inputs, is to do the replace string manipulation first, which has no effect if your other two formats arrive. Then parse all three variations as OffsetDateTime objects. Sometimes programmers tend to over-think a problem, and over-engineer an elaborate solution where a simple one suffices.

Example code:

OffsetDateTime odt1 = OffsetDateTime.parse( "2022-03-12T03:59:59+0000Z".replace( "+0000Z" , "Z" ) ) ;
OffsetDateTime odt2 = OffsetDateTime.parse( "2017-01-04T12:30:00-05:00".replace( "+0000Z" , "Z" ) ) ;
OffsetDateTime odt3 = OffsetDateTime.parse( "2017-03-20T22:05:00Z".replace( "+0000Z" , "Z" ) ) ;

2022-03-12T03:59:59Z

2017-01-04T12:30-05:00

2017-03-20T22:05Z

See this code run live at IdeOne.com.

Trap for DateTimeParseException while parsing to detect yet another unexpected format.

Of course, the best solution is to educate the publisher of your data about consistent use of ISO 8601 formats such as the latter two examples, while avoiding the screwy first example’s format.

Count since epoch

I recommend against tracking time as a count-since-epoch. Such a count is inherently ambiguous and error-prone. Instead, use text in standard ISO 8601 format. But if you insist on a count, here it is.

To get a count of seconds since the epoch reference of 1970-01-01T00:00Z, extract a Instant from the OffsetDateTime, and interrogate.

long secondsSinceEpoch = odt.toInstant().getEpochSecond() ;

Upvotes: 2

stdunbar
stdunbar

Reputation: 17535

That isn't a ISO_DATE_TIME format. That would require something like 2022-03-12T03:59:59+0000 (no 'Z'). A formatter that works looks something like:

import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;


class Scratch {
    public static void main(String[] args) {
        final String inputDate = "2022-03-12T03:59:59+0000Z";

        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .parseCaseInsensitive()
                .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
                .optionalStart()
                .appendPattern(".SSS")
                .optionalEnd()
                .optionalStart()
                .appendZoneOrOffsetId()
                .optionalEnd()
                .optionalStart()
                .appendOffset("+HHMM", "0000")
                .optionalEnd()
                .optionalStart()
                .appendLiteral('Z')
                .optionalEnd()
                .toFormatter();

        long epochSecond = ZonedDateTime.parse(inputDate, formatter).toEpochSecond();

        System.out.println("epochSecond is " + epochSecond);
    }
}

as derived from this post. You can create that formatter in one place and use it over again.

Upvotes: 2

Related Questions