I use format string for DateTimeFormatter: uuuu-MM-dd'T'HH:mm:ssX
which must support all possible formats of timezone offset, including: Z, 00, 00:00, 0000
According to official DateTimeFormatter documentation, 'X' qualifier must match to offset in these formats:
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
but in fact, it doesn't
input string: "2014-01-01T00:30:00+00:00"
result: java.time.format.DateTimeParseException: Text '2014-01-01T00:30:00+00:00' could not be parsed, unparsed text found at index 22
input string: "2014-01-01T00:30:00Z"
result: correct
the code:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssX");
OffsetDateTime parsed = OffsetDateTime.parse(dateTimeAsString, formatter);
JDK 1.8.0_192 (Oracle, not OpenJDK)
This is a little bit complicated. As jvdmr says, the count of Xs matters. XXXXX
will recognize -08:30:15
, but not -083015
. XXXX
will recognize the latter, but not the former.
To take all possible example formats into account, we need to specify different possibilities. This can be done within the format pattern string using square brackets. These enclose optional parts. A little experimentation showed that the following pattern covers all examples:
uuuu-MM-dd'T'HH:mm:ss[XXXXX][XXXX][X]
Let’s try it out:
DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss[XXXXX][XXXX][X]");
for (String dts : new String[] {
"2014-01-01T00:30:00-08:30:15", "2014-01-01T00:30:00-083015",
"2014-01-01T00:30:00-08:30", "2014-01-01T00:30:00-0830",
"2014-01-01T00:30:00-08", "2014-01-01T00:30:00Z",
}) {
System.out.println(OffsetDateTime.parse(dts, formatter));
}
Output from this snippet is:
2014-01-01T00:30-08:30:15 2014-01-01T00:30-08:30:15 2014-01-01T00:30-08:30 2014-01-01T00:30-08:30 2014-01-01T00:30-08:00 2014-01-01T00:30Z
Edit
VelNaga suggests not to hardcode ISO date-time formats. Since writing format pattern strings is error-prone, this can be a good idea. For example:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.appendPattern("[XXXXX][XXXX][X]")
.toFormatter();
Output using this formatter is the same as with using the one above. It’s wordier, but we could easily end up thinking that it’s worth it since it is less error-prone and may be clearer and easier to read.
From the docs (emphasis mine):
Offset X and x: This formats the offset based on the number of pattern letters. One letter outputs just the hour, such as '+01', unless the minute is non-zero in which case the minute is also output, such as '+0130'. Two letters outputs the hour and minute, without a colon, such as '+0130'. Three letters outputs the hour and minute, with a colon, such as '+01:30'. Four letters outputs the hour and minute and optional second, without a colon, such as '+013015'. Five letters outputs the hour and minute and optional second, with a colon, such as '+01:30:15'. Six or more letters throws IllegalArgumentException. Pattern letter 'X' (upper case) will output 'Z' when the offset to be output would be zero, whereas pattern letter 'x' (lower case) will output '+00', '+0000', or '+00:00'.
This also works in reverse for parsing dates. You want to parse both with and without colons, which means you'll have to use optional sections as no single pattern supports this. Try this pattern: "uuuu-MM-dd'T'HH:mm:ss[XXX][XXXX]"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With