4

I need to parse date in Java. I have String value 2018-05-15 09:32:51.550082 +3:00 and I need to parse it into date-time. I tried to parse to ZonedDateTime, but I got an error at index 10. Tried to parse with DateTimeFormatter parser 'yyyy-MM-dd HH:mm:ss.SSSSSS Z' and got error at index 26. Apparently my UTC offset of +3:00 cannot be parsed. How can I do this?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
tomasomsk
  • 81
  • 10
  • wich language ? – Daniel E. May 15 '18 at 07:37
  • Have a look at this, https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html – Nyle Hassan May 15 '18 at 07:38
  • Okay, so after a lot of banging my head against a brick wall - `+3:00` is not a valid value for the formatter, it MUST have a leading zero `+03:00` :/. After I corrected for that, I ended up using `yyyy-MM-dd HH:mm:ss.n z` for `DateTimeFormatter` – MadProgrammer May 15 '18 at 08:03
  • Exactly what MadProgrammer says. You can not parse this date with java's default parsers. Other than writing a custom parser (which I don't recommend) you should programmatically add a leading 0 to your offset and parse it as a `ZonedDateTime` – Ben May 15 '18 at 08:11
  • 2
    ... or get the format correct at it's source – MadProgrammer May 15 '18 at 08:12
  • 1
    That would obviously be the preferred way. I never understand when programs don't just simply abide by ISO 8601 for time formatting. – Ben May 15 '18 at 08:12
  • I believe that neither Java’s standard formatters nor those you can build from a format pAttern string can parse an offset with 1-digit hours such as `+3:00`. `DateTimeFormatterBuilder.appendOffset()` allows you to control the format of the offset, for example `+H:MM:ss` - hour and minute, with second if non-zero, with colon. – Ole V.V. May 15 '18 at 08:14
  • 1
    @OleV.V. good idea but sadly the pattern does not allow `H` only, it has to be `HH` or you run into an `IllegalArgumentException` – Ben May 15 '18 at 08:16
  • 1
    @Ben You are partly correct: it does not work in Java 8, but it does in Java 9 and 10. – Ole V.V. May 15 '18 at 08:21
  • @OleV.V. that's actually very good to know! Thanks! Haven't looked at that particularly in Java 9/10 yet. TIL. – Ben May 15 '18 at 08:22
  • Possible duplicate of [How to parse a date?](https://stackoverflow.com/questions/999172/how-to-parse-a-date) – AxelH May 15 '18 at 08:25
  • @AxelH I was thinking the same at first. The real problem in this question is how to parse the offset with one-digit hours, and the question you are linking to certainly doesn’t address that. It’s not really explained in this question that this is the real problem, though. – Ole V.V. May 15 '18 at 08:40
  • Well @OleV.V., the answer provide the documentation of `SimpleDateFormat` (and other provide the ISO for java.time) where we can found that this offset is not a valid offset (until java 9 based on your answer). This answer partially the "how to parse that format" since this can''t be done directly (not without some modification of the `String`). So I think this would still be a correct answer (we can't write an answer for every possible format that can be written, sometime reading the manual should be enough). But I see what you mean, this might be a borderline duplicate ;) – AxelH May 15 '18 at 08:43

2 Answers2

7

Java 9 and later allows:

    String dateTimeString = "2018-05-15 09:32:51.550082 +3:00";
    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendPattern("uuuu-MM-dd HH:mm:ss.SSSSSS ")
            .appendOffset("+H:MM:ss", "+0:00")
            .toFormatter();
    OffsetDateTime dateTime = OffsetDateTime.parse(dateTimeString, formatter);
    System.out.println(dateTime);

Output:

2018-05-15T09:32:51.550082+03:00

For Java 8 or the ThreeTen Backport you will probably have to prepare your date-time string before parsing it:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSS XXX");
    dateTimeString 
            = dateTimeString.replaceFirst("([+-])(\\d(?::\\d\\d){0,2})$", "$10$2");

The rest is as before and gives the same result. The replaceFirst call inserts a 0 between the + and 3:00, but only if the hours were only one digit. The regular expression is hard to read, so you will prefer the Java 9 solution.

As already discussed in the comments the hard part is the offset of +3:00. Java’s built-in formatters and the format pattern letters you can use for specifying your own format all require two-digit hours in the offset. The DateTimeFormatterBuilder.appendOffset method that I am using allows a limited number of formats to be specified; 9 different formats in Java 8; 22 in Java 9. Link: Documentation of appendOffset().

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Thank you. Can't check it on Java 9, but on Java 8 all works fine. Really cool answer – tomasomsk May 15 '18 at 08:58
  • Good answer! I typically use `+H:mm:ss` i.e. `m` instead of `M`. However, `M` also works correctly. Maybe I haven't spent enough time looking into the documentation and if you have used it knowingly, can you please point me to the documentation? – Arvind Kumar Avinash Dec 14 '22 at 19:31
  • In this case upper case letter means mandatory and lower case means optional. I could not be sure, but since `00` minutes were given in the example, I assumed they were mandatory and used upper case `MM`. Will add link, thanks for asking. @ArvindKumarAvinash – Ole V.V. Dec 14 '22 at 20:37
0

Comment from MadProgrammer is really helpful. Thanks, man. At first I add 0 before 3 in timezone and my string had became '2018-05-15 09:32:51.550082 +03:00' and then I use DateTimeFormatter 'yyyy-MM-dd HH:mm:ss.n z' and all works now. Thanks to all

tomasomsk
  • 81
  • 10
  • That gives me 2018-05-15T09:32:51.000550082+03:00. The decimals on the second are three positions too far to the right. Otherwise fine. And in any case thanks for sharing your solution. – Ole V.V. May 15 '18 at 08:55