So when trying to replace some legacy code using SimpleDateFormat and Date, to use java.time.DateTimeFormatter and LocalDate I ran into a problem. The two date formats are not equivalent. At this point I must say I know the two date types are not the same but the scenario I am in means I never care about the time aspect so can ignore it.
public Date getDate(String value) {
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
try {
return dateFormat.parse(value);
} catch (ParseException e) {
return null;
}
}
public LocalDate getLocalDate(String value) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
try {
return LocalDate.parse(value, formatter);
} catch (DateTimeParseException e) {
return null;
}
}
public void testDates() {
getDate("03/07/2016"); // Sun Jul 03 00:00:00 BST 2016
getDate("3/7/2016"); // Sun Jul 03 00:00:00 BST 2016
getDate("3/7/2016 00:00:00"); // Sun Jul 03 00:00:00 BST 2016
getDate("3/7/2016 00:00:00.0+0100"); // Sun Jul 03 00:00:00 BST 2016
getDate("3/7/2016T00:00:00.0+0100"); // Sun Jul 03 00:00:00 BST 2016
getLocalDate("03/07/2016"); // 2016-07-03
getLocalDate("3/7/2016"); // null
getLocalDate("3/7/2016 00:00:00"); // null
getLocalDate("3/7/2016 00:00:00.0+0100"); // null
getLocalDate("3/7/2016T00:00:00.0+0100"); // null
}
As you can see when the same pattern is used in both formatters the DateTimeFormatter ends up producing nulls where you'd expect to see dates equivalent to that of SDF. In this scenario I would expect the unrequired data to be dropped but it isn't.
So, how do we create a robust date/time parser?!
DateTimeFormatter is a replacement for the old SimpleDateFormat that is thread-safe and provides additional functionality.
Class SimpleDateFormat. Deprecated. A class for parsing and formatting dates with a given pattern, compatible with the Java 6 API.
Yes, it is: DateTimeFormat is thread-safe and immutable, and the formatters it returns are as well. Implementation Requirements: This class is immutable and thread-safe.
ISO_DATE_TIME. The ISO-like date-time formatter that formats or parses a date-time with the offset and zone if available, such as '2011-12-03T10:15:30', '2011-12-03T10:15:30+01:00' or '2011-12-03T10:15:30+01:00[Europe/Paris]'. static DateTimeFormatter.
So there may be other answers to this but what I came up caters for the most extreme case I have. Firstly I reduced dd/MM to d/M. This denotes the minimum number of expected characters so will parse double digits completely fine. Note you could also use new DateTimeFormatterBuilder().parseLenient() but this seemed unnecessary.
Secondly I decided to use the optional clause in the format pattern itself. This allows you to specify which parts may not be provided which is exactly the case I was trying to solve.
Leaving us with:
DateTimeFormatter.ofPattern("d/M/yyyy[' ']['T'][H:mm[:ss[.S]]][X]");
This will now handle providing a date with or without time including a T separator, seconds, millis and zone offset.
With any luck this helps someone else!
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yyyy[' ']['T'][H:mm[:ss[.S]]][X]");
public LocalDate getRobustLocalDate(String value) {
try {
return LocalDate.parse(value, formatter);
} catch (DateTimeParseException e) {
return null;
}
}
@Test
public void testDates() {
getRobustLocalDate("03/07/2016"); // 2016-07-03
getRobustLocalDate("3/7/2016"); // 2016-07-03
getRobustLocalDate("3/7/2016 00:00:00"); // 2016-07-03
getRobustLocalDate("3/7/2016 00:00:00.0+0100"); // 2016-07-03
getRobustLocalDate("3/7/2016T00:00:00.0+0100"); // 2016-07-03
}
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