Search code examples
javadatetime-parsingjava-time

Unable to convert string to LocalDateTime object


I am trying to convert a string to LocaleDateTime object in Java8 as below :

    DateTimeFormatter globalFormat = DateTimeFormatter.ofPattern("yyyyMMddhhmmssSS");
    String input = "2019082905020425";
    LocalDateTime currentDateTime = LocalDateTime.parse(input, globalFormat);

But I am getting below exception, if someone can help me with a solution on the same :

Exception in thread "main" java.time.format.DateTimeParseException:
Text '2019082905020425' could not be parsed at index 0 at
java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1947)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1849)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)at test.main(Test.java:20)

Solution

  • It’s a bug in Java 8.

    Workaround for Java 8

        DateTimeFormatter globalFormat = new DateTimeFormatterBuilder()
                .appendPattern("yyyyMMddHHmmss")
                .appendValue(ChronoField.MILLI_OF_SECOND, 3)
                .toFormatter();
        String input = "2019082905020425";
        String adaptedInput = input + "0";
        LocalDateTime currentDateTime = LocalDateTime.parse(adaptedInput, globalFormat);
        System.out.println("Parsed date and time: " + currentDateTime);
    

    Output from this snippet is (tested on jdk-1.8.0_121):

    Parsed date and time: 2019-08-29T05:02:04.250

    Java 8 cannot separate an integer field like ss and a fractional fields like SS without any separator between them. The workaround is to parse the fraction as an integer too. Your string includes 100ths of seconds, and no integer field for those is built in. So I append an extra 0 (zero) so that we’ve got milliseconds, and then use ChronoField.MILLI_OF_SECOND for parsing.

    Whether it was really a bug can maybe be debated. There never was any strict promise in the docs that it should work, but it seemed to be the expectation of many, and in any case they fixed it in Java 9.

    I have made one more correction, and you will want to check whether this is the correction you want: Lowercase hh is for hour within AM or PM from 01 through 12. If you intended this, you need to specify whether you want AM or PM. Instead I assumed that 05 was an hour of day from 00 through 23. Use uppercase HH for parsing this.

    Edit: use a regular expression? @josejuan advocates a regular expression over the above. It’s an option, and can save us of the explicit formatter completely:

        String input = "2019082905020425";
        String adaptedInput = input.replaceFirst(
                "^(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})$",
                "$1-$2-$3T$4:$5:$6.$7");
        LocalDateTime currentDateTime = LocalDateTime.parse(adaptedInput);
    

    The result is the same as before. For my part I find the latter code quite a lot harder to read and maintain. Also once you migrate to Java 9 or higher, I think that the first snippet above lends itself more directly to going back to the code from which you started, which is what you want in the end. Pick the solution that you prefer.

    The code is working on Java 9 and later

    On Java 9 and later the change from hh to HH is all we need for the code in the question to work fine.

    Links