I have
private static final DateTimeFormatter DATE_PATTERN = new DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd[ ]['T'][HH:mm:ss][.SSSSSS][.SSSSS][.SSSS][.SSS][.SS][.S][X]")
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.parseDefaulting(ChronoField.MILLI_OF_SECOND, 0)
.toFormatter()
.withZone(ZoneOffset.UTC); // assume incoming is UTC
used by
public static Function<String, String> getFormattedDate() {
return dateTime -> {
try {
ZonedDateTime timeRemoved =
ZonedDateTime.parse(dateTime, DATE_PATTERN).truncatedTo(ChronoUnit.DAYS);
return DateTimeFormatter.ofPattern(DISPLAY_DATE_PATTERN).format(timeRemoved);
} catch (Exception e) {
return null;
}
};
}
with a test that fails
public void test_data_patter_with_ms(){
String formattedDate = DateTimeUtil.getFormattedDate().apply("2021-04-24T06:57:06.850");
assertThat(formattedDate, is("2021-04-24T00:00:00Z"));
}
with the exception
java.time.format.DateTimeParseException: Text '2021-04-24T06:57:06.850' could not be parsed: Conflict found: NanoOfSecond 850000000 differs from NanoOfSecond 0 while resolving MilliOfSecond
If I comment out .parseDefaulting(ChronoField.MILLI_OF_SECOND, 0) it works, but I'm not really sure I understand why and I don't like fixing issues and not understanding the why I fixed it.
Note that the
S
pattern specifier corresponds to theNANO_OF_SECOND
field. DocumentationTherefore, when you do
parseDefaulting(ChronoField.MILLI_OF_SECOND, 0)
, the default value 0 is always used. From the documentation ofparseDefaulting
:In your case, it is always the case that
MILLI_OF_SECOND
"has no associated value", because your pattern parses theNANO_OF_SECOND
field. There is noMILLI_OF_SECOND
component in your pattern in the first place!And only then comes the resolution step of the parsing process. This basically figures out the values of unknown fields from the known fields, and also checks that the values of the fields agree with each other. This is where the algorithm finds out that somehow your date has a
MILLI_OF_SECOND
of 0 due to yourparseDefaulting
call, but also aNANO_OF_SECOND
of 850000000 because of what's in your string. This is a contradiction, and so an exception is thrown.I think you should have called
parseDefaulting
withNANO_OF_SECOND
instead:This way, when any of the
.SSS
options appear in the string, the default value won't be used.Side note:
It is possible to change your pattern so that it parses the
MILLI_OF_SECOND
field, usingappendValue
: