endryha
endryha

Reputation: 7256

DateTimeFormatter cannot parse string produced by itself

I have issue with parsing following date format:

2017-03-27T08:27:43.326TGMT-05:00

I have code where DateTimeFormatter produces string out of ZonedDateTime and as a next step I am trying to parse this string again and create ZonedDateTime instance, however it doesn't work as I expected:

String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'T'ZZZZ";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
String dateStr = ZonedDateTime.now().format(formatter);

System.out.println(dateStr);

ZonedDateTime dateParsed = ZonedDateTime.parse(dateStr, formatter);
System.out.println(dateParsed);

The code above produces:

2017-03-27T08:27:43.326TGMT-05:00
java.time.format.DateTimeParseException: Text '2017-03-27T08:27:43.326TGMT-05:00' could not be parsed: String index out of range: 33

    at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
    at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)

Looking for any insight on how to come up with working pattern that is capable to create ZonedDateTime instance out of 2017-03-27T08:27:43.326TGMT-05:00

Upvotes: 3

Views: 1115

Answers (2)

user7605325
user7605325

Reputation:

To understand what the ZZZZ pattern does, take a look at the JDK docs, where you can see its meaning:

Pattern  Count  Equivalent builder methods
-------  -----  --------------------------
ZZZZ     4      appendLocalizedOffset(TextStyle.FULL);

So, ZZZZ is equivalent to appendLocalizedOffset(TextStyle.FULL). If you look at appendLocalizedOffset method:

Appends the localized zone offset, such as 'GMT+01:00', to the formatter. This appends a localized zone offset to the builder, the format of the localized offset is controlled by the specified style to this method:

  • full - formats with localized offset text, such as 'GMT, 2-digit hour and minute field, optional second field if non-zero, and colon.

And note that it has different behaviours when parsing and formatting:

During formatting, the offset is obtained using a mechanism equivalent to querying the temporal with TemporalQueries.offset(). If the offset cannot be obtained then an exception is thrown unless the section of the formatter is optional.

During parsing, the offset is parsed using the format defined above. If the offset cannot be parsed then an exception is thrown unless the section of the formatter is optional.

So, it seems that ZZZZ pattern adds the GMT-05:00 when formatting (without the seconds when it's zero), but when parsing, it tries to parse the seconds (even when they're not present). That's why, when parsing, you need to add :00 in the end (as @freedev did in his answer).

As ZZZZ seems to be the only pattern that adds GMT before the offset (all the others just add the offset without GMT), I think the only solution is to add it by hand. You can do it using java.time.format.DateTimeFormatterBuilder class:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // append the yyyy-MM-dd'T'HH:mm:ss.SSS pattern
    .appendPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
    // append "T" and "GMT"
    .appendLiteral("TGMT")
    // append the offset (-05:00)
    .appendOffsetId()
    .toFormatter();

With this formatter, your code will work as expected:

String dateStr = ZonedDateTime.now().format(formatter);
System.out.println(dateStr);

ZonedDateTime dateParsed = ZonedDateTime.parse(dateStr, formatter);
System.out.println(dateParsed);

Output:

2017-05-25T08:42:17.799TGMT-05:00

2017-05-25T08:42:17.799-05:00


Testing with your input:

String input = "2017-03-27T08:27:43.326TGMT-05:00";
System.out.println(input);
System.out.println(ZonedDateTime.parse(input, formatter));

Output:

2017-03-27T08:27:43.326TGMT-05:00

2017-03-27T08:27:43.326-05:00


PS: In cases like this I prefer to use a DateTimeFormatterBuilder, but you can do the same using DateTimeFormatter.ofPattern directly:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'TGMT'xxx");

This works exactly the same way.

Upvotes: 0

freedev
freedev

Reputation: 30027

Just for the sake to understand what's wrong I had a look at source code of DateTimeFormatterBuilder.java:3563 where the exception was raised.

I saw there is a piece of code that parse even the seconds parts after GMT-05:00.

So it will work only if you add the seconds part...

ZonedDateTime dateParsed = ZonedDateTime.parse(dateStr+":00", formatter);

Upvotes: 2

Related Questions