0

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)
psingh
  • 64
  • 1
  • 13
  • 1
    Two issues I can see. First, `LocalDateTime` can't determine if the input is AM or PM. If I use `HH` instead of `hh` I can make it work. Second, you format doesn't have 3 places for `SSS`, it only has two, so, if I use `yyyyMMddHHmmssSS` instead I can get it to work – MadProgrammer Sep 14 '19 at 07:39
  • So if i use yyyyMMddHHmmssSS in my datetimeformatter will it work ? – psingh Sep 14 '19 at 07:42
  • Based in the available input, yes, I was able to get it to parse the value - as to if it will return the correct result, I can't say, as there is a significant difference between `HH` and `hh` – MadProgrammer Sep 14 '19 at 07:44
  • 3
    you must use `HH`, `hh` would only work if there is `am` or `pm` in the input and format (or that like) – user85421 Sep 14 '19 at 07:50
  • There is no way to parse milliseconds part as above. You should have some thing between the second and millisecond **'yyyyMMddHHmmss SS'** for **'20190829050204 25'** or **'yyyyMMddHHmmss.SS'** for **'20190829050204.25'** – ChickenSoups Sep 14 '19 at 07:59
  • 1
    @ChickenSoups `SS` is not milliseconds, it's between 1 and 9 digits of fractional seconds, and `LocalDateTime.parse("2019082905020425", DateTimeFormatter.ofPattern("yyyyMMddHHmmssSS"))` runs fine, returning `2019-08-29T05:02:04.250`. Don't confuse the pattern of the new Java 8 Time API with the old `SimpleDateFormat` pattern, where `SSS` *is* milliseconds only. – Andreas Sep 14 '19 at 09:46
  • 1
    Ole V.V. is wrong, in Java8 you can't parse that expression (in particular) directly using DateTimeFormatter (curiously he himself has put the proof of it), you must to change the pattern (eg. adding spaces) or use regex, but how easy it is to vote negative or duplicate... :/ – josejuan Sep 14 '19 at 13:42
  • @josejuan The value can be parsed, the problem is, there isn't enough context for the current format pattern (ie it doesn't know if it's AM or PM) - this then raises questions about what the time value actual is and whether a different pattern (ie using `HH` instead) would work in the case of the OP. The problem is, there simply isn't enough context to the question to provide a satisfactory answer for using `DateTimeFormatter` or reg exp. – MadProgrammer Sep 14 '19 at 22:47
  • @josejuan Personally, I would avoid using reg exp for these types of operations as it doesn't provide validation - and even if you did break the string down into it's constituent parts, you would still have issues creating a `LocalDateTime` value from them, because you have no context for which part of the day the hour represents - so we'd be back to square one - we'd need more information. You are, of course, free to have your own opinion – MadProgrammer Sep 14 '19 at 22:48
  • I am sorry, I made a mistake. The code in the question runs fine on Java 9 and later when you correct lowercase `hh` into uppercase `HH`. To parse the format `2019082905020425` on Java 8 you would need to create a `TemporalField` for 100ths of seconds. It would be way too complicated. My solution would be appending a `0` (zero) and parsing using the workaround linked to with `appendValue(ChronoField.MILLI_OF_SECOND, 3)`. (I didn’t downvote anything here.) Do we want the question reopened? – Ole V.V. Sep 15 '19 at 05:50
  • Yes. it would help if you can please remove the duplicate mark. – psingh Sep 15 '19 at 06:29

1 Answers1

3

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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Okay, now explain to the programmer of the future all this *workaround* and how to evolve to the new functionality. Conclusion: use a simple regular expression and write "due to a bug in Java 8 (http://...)". – josejuan Sep 15 '19 at 09:08