DateTimeFormatter.parse(s) throws when it reaches the end of s: why?

James Youngman :

I've been trying to use DateTimeFormatter to parse date/time strings of the form "20141112152340". When I call formatter.parse(input); it raises an exception:

java.time.format.DateTimeParseException: Text '20141112152340' could not be parsed at index 14

Here is a minimal complete reproducer:

package com.example.minimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.ResolverStyle;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;

public class Reproducer {
    public static void main(String[] args) {
        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendValue(ChronoField.YEAR) // 4
            .appendValue(ChronoField.MONTH_OF_YEAR) // 2
            .appendValue(ChronoField.DAY_OF_MONTH) // 2
            .appendValue(ChronoField.HOUR_OF_DAY) // 2
            .appendValue(ChronoField.MINUTE_OF_HOUR) // 2
            .appendValue(ChronoField.SECOND_OF_MINUTE) // 2
            .toFormatter()
            // Using the default ResolverStyle appears to make no difference.
            .withResolverStyle(ResolverStyle.STRICT); // expect 14 characters
        String input = "20141112152340";
        System.out.printf("length of input is %02d:               [%s]; format is %s\n", input.length(), input, formatter.toString());
        // When the minute of hour is < 10, it is rendered without zero-padding.   Perhaps this
        // is related.  But, the same problem is reproducible on input strings that do not contain zeroes.
        System.out.printf("Current local time in that format is [%s]\n", formatter.format(LocalDateTime.now()));
        // The following line throws java.time.format.DateTimeParseException: Text '20140816152340' could not be parsed at index 14
        TemporalAccessor parsed = formatter.parse(input);
        // NOTREACHED
        LocalDateTime stamp = parsed.query(LocalDateTime::from);
        System.out.println("Time stamp is " + stamp.toString());
    }
}

Here is the output of this program:

length of input is 14:               [20141112152340]; format is Value(Year)Value(MonthOfYear)Value(DayOfMonth)Value(HourOfDay)Value(MinuteOfHour)Value(SecondOfMinute)
Current local time in that format is [20191010183335]
Exception in thread "main" java.time.format.DateTimeParseException: Text '20141112152340' could not be parsed at index 14
    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1874)
    at com.example.minimal.Reproducer.main(Reproducer.java:28)

As far as I can tell, the parser is bailing at the point it reaches the end of the string. On the offchance that this happens because it's looking for more data I tried switching the resolver style from the default to strict, but this seems to make no difference.

My question is, why is this happening, and how do I correct my code so that it can parse this date/time representation?

Malt :

The following formatter does work for me:

DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

The toString() of both formatters reveals the difference:

Value(Year)Value(MonthOfYear)Value(DayOfMonth)Value(HourOfDay)Value(MinuteOfHour)Value(SecondOfMinute)

vs

Value(YearOfEra,4,19,EXCEEDS_PAD)Value(MonthOfYear,2)Value(DayOfMonth,2)Value(HourOfDay,2)Value(MinuteOfHour,2)Value(SecondOfMinute,2)

As you can see, the second formatter, which is the working version, seems to have explicit field widths as well as explicit SignStyle

We can create the same formatter using the builder:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .appendValue(ChronoField.YEAR_OF_ERA, 4, 19, SignStyle.EXCEEDS_PAD)
        .appendValue(ChronoField.MONTH_OF_YEAR, 2)
        .appendValue(ChronoField.DAY_OF_MONTH, 2)
        .appendValue(ChronoField.HOUR_OF_DAY, 2)
        .appendValue(ChronoField.MINUTE_OF_HOUR, 2)
        .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
        .toFormatter()
        .withResolverStyle(ResolverStyle.STRICT);

However it looks like you can replace YEAR_OF_ERA with YEAR with a similar result.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324077&siteId=1