We try to parse the following ISO 8601 DateTime String with timezone offset:
final String input = "2022-03-17T23:00:00.000+0000";
OffsetDateTime.parse(input);
LocalDateTime.parse(input, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Both approaches fail (which makes sense as OffsetDateTime
also use the DateTimeFormatter.ISO_OFFSET_DATE_TIME
) because of the colon in the timezone offset.
java.time.format.DateTimeParseException: Text '2022-03-17T23:00:00.000+0000' could not be parsed at index 23
But according to Wikipedia there are 4 valid formats for a timezone offset:
<time>Z
<time>±hh:mm
<time>±hhmm
<time>±hh
Other frameworks/languages can parse this string without any issues, e.g. the Javascript Date()
or Jacksons ISO8601Utils
(they discuss this issue here)
Now we could write our own DateTimeFormatter
with a complex RegEx, but in my opinion the java.time
library should be able to parse this valid ISO 8601 string by default as it is a valid one.
For now we use Jacksons ISO8601DateFormat
, but we would prefer to use the official date.time
library to work with. What would be your approach to tackle this issue?
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:
.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.