
I want to write a generic function where for these valid date format MM/dd/yyyy, M/dd/yyyy or M/d/yy
Question:
I have a flat file with below data
2/12/2016
2/3/2017
12/23/2017
04/23/2017
first is in M/dd/yyyy, second is in M/d/yyyy and rest of them having date format MM/dd/yyyy. I am using below function to check data validity for above data file.
public static boolean isValidDateFormat(String format, String value)
{
boolean Retval = false;
Date date = null;
SimpleDateFormat sdf = new SimpleDateFormat(format);
date = sdf.parse(value);
if (value.equals(sdf.format(date))) {
Retval= true;
}
return Retval;
}
I have to check date validity within loop and can pass only one date format like isValidDateFormat("MM/dd/yyyy", "2/12/2016")
. Above function return false for first two values
and true for rest of them
.
I want to write a generic function where for these valid date format MM/dd/yyyy, M/dd/yyyy or M/d/yyyy, function return true.
Answer1:Why not just allow to provide to method more than one format?
public static boolean isValidDateFormat(String value, String... formats)
{
for(String format : formats) {
SimpleDateFormat sdf = new SimpleDateFormat(format);
Date date = sdf.parse(value);
if(value != null && value.equals(sdf.format(date)) ) {
return true;
}
}
return false;
}
then you can keep your allowed formats in collection and provide it to the method
String[] formats = new String[]{"dd.mm.yyyy", "dd/mm/yyyy"};
if(isValidDateFormat("12.12.2014", formats) {
//do sth
}
Answer2:Using the java.time
API, your method could be rewritten to this
public static boolean isValidDateFormat(String value){
try {
LocalDate.parse(value, DateTimeFormatter.ofPattern("M/d/uuuu").withResolverStyle(ResolverStyle.STRICT));
} catch (DateTimeParseException e) {
return false;
}
return true;
}
This will return true
for MM/dd/yyyy, M/dd/yyyy, MM/d/yyyy, M/d/yyyy.
We need to specify the ResolveStyle explicitly here. Java uses ResolverStyle.SMART
as a default. This allows you to enter days in the range of 1 - 31 for every month, and Java converts invalid days (30th of February for example) to the last valid day of the month.
We don't need that "smart" behaviour here, hence the ResolverStyle.STRICT
.
This is not as neat or short, but works as well:
public static boolean isValidDateFormat(String value){
String[] sa = value.split("/");
if (sa[2].length() != 4) return false;
try {
LocalDate.of(Integer.parseInt(sa[2]), Integer.parseInt(sa[0]), Integer.parseInt(sa[1]));
} catch (DateTimeException e) {
return false;
}
return true;
}
Answer3:An alternative to using a validation based on SimpleDateFormat
s would be using a regex which would accept the 3 different formats :
value.matches("(0?\\d|1[0-2])/([0-2]?\\d|3[01])/\\d{4}")
I expect it to be much more efficient, however it has multiple problems :
<ul><li>it does accept some invalid dates that I think would be rejected by the SimpleDateFormat
, such as 02/31/2017
you should then change your method's signature since the format
argument doesn't make sense anymore (unless you want to pass the regex there)
it loses some meaning : regexp matches any text, while a SimpleDateFormat
explicitly matches dates (represented as text)
The favourable solution would be to make the date entries consistent in the flat file in the first place so that you will only have to use one DateFormat. You could perhaps write a utility to prefix any single digit month or day entries with 0s.
Alternatively, you could employ a utility method to sanity check the date before selecting a DateFormat. For example, it could split the String at the '/' character, giving you an array of Strings. You could then check the length of the first two tokens of the array to determine which Date format you need to use.