Handling multiple formats in DateTimeFormatter
Asked Answered
D

3

5

I am developing using Java 8 a function that must handle the conversion from String to LocalDateTime of the following dates:

  • 2019-06-20 12:18:07.207 +0000 UTC
  • 2019-06-20 12:18:07.20 +0000 UTC
  • 2019-06-20 12:18:07.2 +0000 UTC
  • 2019-06-20 12:18:07 +0000 UTC

The strings are produced from an external library that I cannot change.

Following the suggestions given in the SO answer Optional parts in SimpleDateFormat, I tried using the optional formatting offered by the type DateTimeFormatter, using the characters [ and ]. I tried the following patterns:

  • yyyy-MM-dd HH:mm:ss[.S[S[S]]] Z z
  • yyyy-MM-dd HH:mm:ss[.S[S][S]] Z z

However, neither of them works.

Any suggestion?

Dalury answered 19/8, 2019 at 8:50 Comment(0)
D
9

You can build the pattern using DateTimeFormatterBuilder and reuse ISO_LOCAL_DATE and ISO_LOCAL_TIME constants:

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ISO_LOCAL_DATE)
            .appendLiteral(" ")
            .append(DateTimeFormatter.ISO_LOCAL_TIME)
            .appendPattern("[ Z z]")
            .toFormatter();

    ZonedDateTime dt = ZonedDateTime.parse(date, formatter);

The trick is that DateTimeFormatter.ISO_LOCAL_TIME handles the different number of digit used to represent milliseconds its own. From DateTimeFormatter.ISO_LOCAL_TIME JavaDoc:

This returns an immutable formatter capable of formatting and parsing the ISO-8601 extended local time format. The format consists of:
[..]
One to nine digits for the nano-of-second. As many digits will be output as required.

Dragon answered 19/8, 2019 at 11:48 Comment(6)
Sorry, but your answer did not respond to my question. The optional and variable part is the milliseconds' block, not the offset.Dalury
Doesn't ISO_LOCAL_TIME itself take care of that optional part of milliseconds? Try running the examples with this code.Dragon
Yep, you're right. Sorry. I added some useful information to your answer.Dalury
Thanks. You OK with ZonedDateTime, right? I think the requirement of having LocalDateTime in the question conflicts with zone specific text that needs to be parsed.Dragon
Don't worry. The problem was with the optional part.Dalury
Your DateTimeFormatter serves parsing to ZonedDateTime as well as LocalDateTime e.g. you can also use it as LocalDateTime dt = LocalDateTime.parse("2019-06-20 12:18:07.207 +0000 UTC", formatter);Dexedrine
H
5

I think it is better to use a DateTimeFormatterBuilder for that purpose. For the optional parts just use one of the follwing methods :

  1. OptionalStart() & OptionalEnd()
  2. Append your whole optional pattern with appendOptional()

Here is an example :

DateTimeFormatter formatter = DateTimeFormatter.ofPattern(""
    + "[yyyy-MM-dd HH:mm:ss.SSS Z z]"
    + "[yyyy-MM-dd HH:mm:ss.SS Z z]"
    + "[yyyy-MM-dd HH:mm:ss.S Z z]"
    + "[yyyy-MM-dd HH:mm:ss Z z]"
);

Also, you can create a dtf for each optional and append them with appendOptional() and the the DateTimeFormatterBuilder

for example :

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .appendValue(HOUR_OF_DAY,2)
    .optionalStart()
    .appendValue(MINUTE_OF_HOUR,2)
    .optionalEnd()
    .optionalStart()
    .appendValue(SECOND_OF_MINUTE,2)
    .optionalEnd()
    .toFormatter();

This code is not tested but try to build your optional pattern each time in a start/end optional blocks.

Heavyhearted answered 19/8, 2019 at 9:27 Comment(2)
In the first part of the answer, you cited the DateTimeFormatterBuilder, but you don't use it in the example. Can you modify the answer?Dalury
I added an brief example of how it may works with the builder, i didn't test it. Did the first way work?Heavyhearted
M
2

Here is my example that I use in one app that takes different formats as you can see millisecond is optional and also 'Z' at end.

new DateTimeFormatterBuilder()
      .appendPattern("yyyy-MM-dd'T'HH:mm:ss")
      .optionalStart()
      .appendLiteral('.')
      .appendValue(ChronoField.MILLI_OF_SECOND, 1, 3, SignStyle.NORMAL)
      .optionalEnd()
      .optionalStart()
      .appendLiteral('Z')
      .optionalEnd()
      .toFormatter

appendPattern is static value, other at inside optional start and end, very simple to build such pattern ( if you know of course how to do that ;) )

Marrin answered 3/11, 2022 at 8:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.