Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Literal dash added to DateTimeFormatterBuilder causes parsing to fail

I'm trying to create a DateTimeFormatter to match the following example (it's actually slightly more complex than this but that shouldn't matter).

20180302-17:45:21

I've written the following but it results in an exception:

new DateTimeFormatterBuilder()
    .append(DateTimeFormatter.BASIC_ISO_DATE)
    .appendLiteral('-')
    .append(DateTimeFormatter.ISO_LOCAL_TIME)
    .toFormatter()
    .parse("20180302-17:45:21");

The exception is:

Exception in thread "main" java.time.format.DateTimeParseException: Text '20180302-17:45:21' could not be parsed at index 11
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1988)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1816)

It appears to be failing on the colon between 17:45 and DateTimeFormatterBuilder.appendLiteral doesn't give any clues.

If I change the literal to another character, let's say m, then it works fine:

new DateTimeFormatterBuilder()
    .append(DateTimeFormatter.BASIC_ISO_DATE)
    .appendLiteral('m')
    .append(DateTimeFormatter.ISO_LOCAL_TIME)
    .toFormatter()
    .parse("20180302m17:45:21");

What's going on here? How can I fix it, assuming I can't change the format?

Comments suggest this might be version dependent. I'm using JDK 9.0.1 and it's been reproduced on 9.0.4.

like image 885
Michael Avatar asked Mar 05 '18 11:03

Michael


People also ask

What is DateTimeFormatter in java?

LocalTime. Represents a time (hour, minute, second and nanoseconds (HH-mm-ss-ns)) LocalDateTime. Represents both a date and a time (yyyy-MM-dd-HH-mm-ss-ns) DateTimeFormatter.

What is DateTimeFormatter Iso_date_time?

static DateTimeFormatter. ISO_DATE_TIME. The ISO-like date-time formatter that formats or parses a date-time with the offset and zone if available, such as '2011-12-03T10:15:30', '2011-12-03T10:15:30+01:00' or '2011-12-03T10:15:30+01:00[Europe/Paris]'.

What is the role of the DateTimeFormatter type?

The DateTimeFormatter class is used to both parse and format dates according to specified Date and Time Patterns. Use parse(...) method to convert from String to Date/Time classes, use format(...) method to convert from Date/Time into String.

Is date time formatter thread safe?

DateTimeFormatter is immutable and thread-safe. DateTimeFormatter formats a date-time using user defined format such as "yyyy-MMM-dd hh:mm:ss" or using predefined constants such as ISO_LOCAL_DATE_TIME. A DateTimeFormatter can be created with desired Locale, Chronology, ZoneId, and DecimalStyle.


1 Answers

This has got to do with the fact that DateTimeFormatter.BASIC_ISO_DATE includes an optional offset ID. Apparently your formatter parses -17 as an offset and then objects because there is a colon where the format requires a hyphen.

When you use m instead, this cannot be parsed as an offset and therefore matches the literal m in the format, and everything works.

I tried using uppercase Z. Z can be an offset ID too.

new DateTimeFormatterBuilder()
    .append(DateTimeFormatter.BASIC_ISO_DATE)
    .appendLiteral('Z')
    .append(DateTimeFormatter.ISO_LOCAL_TIME)
    .toFormatter()
    .parse("20180302Z17:45:21");

Now I got java.time.format.DateTimeParseException: Text '20180302Z17:45:21' could not be parsed at index 9. Index 9 us right after the Z, so it seems the formatter parses the offset and then tries to find the literal Z where the 17 is.

EDIT: And the solution? Instead of using BASIC_ISO_DATE append a pattern:

.appendPattern("uuuuMMdd")

Now parsing works also on Java 9.0.4.

EDIT: Further to illustrate the optionality of the offset:

System.out.println(
    LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)
);
System.out.println(
    OffsetDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE)
);

This printed

20180305
20180305+0100

So in the first case, where no offset is available, it just leaves it out. In the second case, where one is available, it is also printed (without colon).

Open question: Why does it work in Java 8? Is this really a bug?

Quote:

  • If the offset is not available to format or parse then the format is complete.
  • The offset ID without colons. If the offset has seconds then they will be handled even though this is not part of the ISO-8601 standard. The offset parsing is lenient, which allows the minutes and seconds to be optional. Parsing is case insensitive.

From the documentation of BASIC_ISO_DATE

like image 117
Ole V.V. Avatar answered Sep 24 '22 23:09

Ole V.V.