I find it curious that the most obvious way to create Date
objects in Java has been deprecated and appears to have been "substituted" with a not so obvious to use lenient calendar.
How do you check that a date, given as a combination of day, month, and year, is a valid date?
For instance, 2008-02-31 (as in yyyy-mm-dd) would be an invalid date.
Here is I would check the date format:
As shown by @Maglob, the basic approach is to test the conversion from string to date using SimpleDateFormat.parse. That will catch invalid day/month combinations like 2008-02-31.
However, in practice that is rarely enough since SimpleDateFormat.parse is exceedingly liberal. There are two behaviours you might be concerned with:
Invalid characters in the date string Surprisingly, 2008-02-2x will "pass" as a valid date with locale format = "yyyy-MM-dd" for example. Even when isLenient==false.
Years: 2, 3 or 4 digits? You may also want to enforce 4-digit years rather than allowing the default SimpleDateFormat behaviour (which will interpret "12-02-31" differently depending on whether your format was "yyyy-MM-dd" or "yy-MM-dd")
A Strict Solution with the Standard Library
So a complete string to date test could look like this: a combination of regex match, and then a forced date conversion. The trick with the regex is to make it locale-friendly.
Note that the regex assumes the format string contains only day, month, year, and separator characters. Aside from that, format can be in any locale format: "d/MM/yy", "yyyy-MM-dd", and so on. The format string for the current locale could be obtained like this:
Joda Time - Better Alternative?
I've been hearing about joda time recently and thought I'd compare. Two points:
It's quite simple to use:
I suggest you to use
org.apache.commons.validator.GenericValidator
class from apache.GenericValidator.isDate(String value, String datePattern, boolean strict);
Note: strict - Whether or not to have an exact match of the datePattern.
This is working great for me. Approach suggested above by Ben.
tl;dr
Use the strict mode on
java.time.DateTimeFormatter
to parse aLocalDate
. Trap for theDateTimeParseException
.After parsing, you might check for reasonable value. For example, a birth date within last one hundred years.
Avoid legacy date-time classes
Avoid using the troublesome old date-time classes shipped with the earliest versions of Java. Now supplanted by the java.time classes.
LocalDate
&DateTimeFormatter
&ResolverStyle
The
LocalDate
class represents a date-only value without time-of-day and without time zone.The
java.time.DateTimeFormatter
class can be set to parse strings with any of three leniency modes defined in theResolverStyle
enum. We insert a line into the above code to try each of the modes.The results:
ResolverStyle.LENIENT
ld: 2000-03-02
ResolverStyle.SMART
ld: 2000-02-29
ResolverStyle.STRICT
ERROR: java.time.format.DateTimeParseException: Text '31/02/2000' could not be parsed: Invalid date 'FEBRUARY 31'
We can see that in
ResolverStyle.LENIENT
mode, the invalid date is moved forward an equivalent number of days. InResolverStyle.SMART
mode (the default), a logical decision is made to keep the date within the month and going with the last possible day of the month, Feb 29 in a leap year, as there is no 31st day in that month. TheResolverStyle.STRICT
mode throws an exception complaining that there is no such date.All three of these are reasonable depending on your business problem and policies. Sounds like in your case you want the strict mode to reject the invalid date rather than adjust it.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as
java.util.Date
,Calendar
, &SimpleDateFormat
.The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for
java.sql.*
classes.Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as
Interval
,YearWeek
,YearQuarter
, and more.You can use SimpleDateFormat
For example something like: