I am currently working with Java's DateTimeFormatter
to parse ISO 8601 formatted timestamps, particularly those containing fractional seconds. While experimenting with different timestamp formats, I noticed some unexpected behavior regarding how the formatter handles optional fractional seconds.
Specifically, I am curious about the leniency of the parser when it comes to the number of digits in the fractional seconds. My implementation allows for timestamps with 9 digits for fractional seconds, yet the parser successfully handles timestamps with only 8 digits while failing for those with 7 or fewer. This has led me to wonder if there is an underlying reason for this behavior, whether it is part of the design of the DateTimeFormatter, and if it is documented anywhere.
I wrote a test using the following code:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public class DateTimeExample {
public static void main(String[] args) {
String[] timestamps = {
"2023-10-05T15:14:29.123456789Z", // 9 digits
"2023-10-05T15:14:29.12345678Z", // 8 digits
"2023-10-05T15:14:29.1234567Z", // 7 digits
"2023-10-05T15:14:29.123456Z", // 6 digits
"2023-10-05T15:14:29.12345Z", // 5 digits
"2023-10-05T15:14:29.1234Z", // 4 digits
"2023-10-05T15:14:29.123Z", // 3 digits
"2023-10-05T15:14:29.12Z", // 2 digits
"2023-10-05T15:14:29.1Z", // 1 digit
"2023-10-05T15:14:29Z" // no fractional seconds
};
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSSSSSSSS]'Z'");
for (String timestamp : timestamps) {
try {
LocalDateTime dateTime = LocalDateTime.parse(timestamp, formatter);
System.out.println("Parsed date: " + dateTime);
} catch (DateTimeParseException e) {
System.err.println("Failed to parse: " + timestamp + " - " + e.getMessage());
}
}
}
}
Observations
When I run this code, this is the output:
Parsed date: 2023-10-05T15:14:29.123456789
Parsed date: 2023-10-05T15:14:29.123456780
Failed to parse: 2023-10-05T15:14:29.1234567Z - Text '2023-10-05T15:14:29.1234567Z' could not be parsed at index 19
Failed to parse: 2023-10-05T15:14:29.123456Z - Text '2023-10-05T15:14:29.123456Z' could not be parsed at index 19
Failed to parse: 2023-10-05T15:14:29.12345Z - Text '2023-10-05T15:14:29.12345Z' could not be parsed at index 19
Failed to parse: 2023-10-05T15:14:29.1234Z - Text '2023-10-05T15:14:29.1234Z' could not be parsed at index 19
Failed to parse: 2023-10-05T15:14:29.123Z - Text '2023-10-05T15:14:29.123Z' could not be parsed at index 19
Failed to parse: 2023-10-05T15:14:29.12Z - Text '2023-10-05T15:14:29.12Z' could not be parsed at index 19
Failed to parse: 2023-10-05T15:14:29.1Z - Text '2023-10-05T15:14:29.1Z' could not be parsed at index 19
Parsed date: 2023-10-05T15:14:29
It successfully parses timestamps with 9 digits for fractional seconds or no fractional part, which is the expected behaviour. But why does it also work with 8 digits for fractional part? My conclusion from this behaviour is that the DateTimeFormatter is lenient with upto one extra digit in the pattern. Is that correct, if so, are there any relevant documentations that I can refer?
setLenient
before adding the part of the fractional seconds if you want it to parse varying lengths, or use theappendValue
of the builder that specifies the minimum and maximum number of digits. – CowbaneDateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.n]'Z'");
– Footlen
is not the right format option though, because then in 2023-10-05T15:14:29.1Z the.1
is 1 nanosecond, instead of 100 milliseconds. – Cowbane..:29.1
mean 29 seconds and 100 milliseconds (100000000 nanoseconds),..:29.123
means 29 seconds and 123 milliseconds (123000000 nanoseconds), and..:29.123456789
means 29 seconds and 123456789 nanoseconds (especially given the OP says they want to use ISO 8601, as that defines them as decimal fractions). – Cowbane