Java 8 Date and Time: parse ISO 8601 string without colon in offset [duplicate]

拜拜、爱过 提交于 2019-11-26 16:40:38

If you want to parse all valid formats of offsets (Z, ±hh:mm, ±hhmm and ±hh), one alternative is to use a java.time.format.DateTimeFormatterBuilder with optional patterns (unfortunatelly, it seems that there's no single pattern letter to match them all):

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // date/time
    .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
    // offset (hh:mm - "+00:00" when it's zero)
    .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
    // offset (hhmm - "+0000" when it's zero)
    .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
    // offset (hh - "Z" when it's zero)
    .optionalStart().appendOffset("+HH", "Z").optionalEnd()
    // create formatter
    .toFormatter();
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+0000", formatter));
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+00", formatter));
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000+00:00", formatter));
System.out.println(OffsetDateTime.parse("2022-03-17T23:00:00.000Z", formatter));

All the four cases above will parse it to 2022-03-17T23:00Z.


You can also define a single string pattern if you want, using [] to delimiter the optional sections:

// formatter with all possible offset patterns
DateTimeFormatter formatter = DateTimeFormatter
    .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS[xxx][xx][X]");

This formatter also works for all cases, just like the previous formatter above. Check the javadoc to get more details about each pattern.


Notes:

  • A formatter with optional sections like the above is good for parsing, but not for formatting. When formatting, it'll print all the optional sections, which means it'll print the offset many times. So, to format the date, just use another formatter.
  • The second formatter accepts exactly 3 digits after the decimal point (because of .SSS). On the other hand, ISO_LOCAL_DATE_TIME is more flexible: the seconds and nanoseconds are optional, and it also accepts from 0 to 9 digits after the decimal point. Choose the one that works best for your input data.

You don't need to write a complex regex - you can build a DateTimeFormatter that will work with that format easily:

DateTimeFormatter formatter =
    DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX", Locale.ROOT);

OffsetDateTime odt = OffsetDateTime.parse(input, formatter);

That will also accept "Z" instead of "0000". It will not accept "+00:00" (with the colon or similar. That's surprising given the documentation, but if your value always has the UTC offset without the colon, it should be okay.

I wouldn't call it a solution but a workaround. SimpleDateFormat's Z template supports the timezone-syntax you showed, so you can do something like this:

final String input = "2022-03-17T23:00:00.000+0000";

try {
    OffsetDateTime.parse(input);
    LocalDateTime.parse(input, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
catch (DateTimeParseException e) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SZ", Locale.GERMANY);
    sdf.parse(input);
}

You're still using official libraries shipped with the JVM. One isn't part of the date.time-library, but still ;-)

Since it is without colon, can you use your own format string :

final String input = "2022-03-17T23:00:00.000+0000";

    DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
    Date parsed = df.parse(input);
    System.out.println(parsed);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!