How to Sanity Check a Date in Java

How to sanity check a date in Java

The current way is to use the calendar class. It has the setLenient method that will validate the date and throw and exception if it is out of range as in your example.

Forgot to add:
If you get a calendar instance and set the time using your date, this is how you get the validation.

Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
cal.getTime();
}
catch (Exception e) {
System.out.println("Invalid date");
}

Check if a string is a valid date

I would keep the try-catch block as small as possible. Also, in case there is a problem parsing the string, I would return Optional<Date> instead of the newly created date object.

public static Optional<Date> getDateFromString(String s) {
Date date = null;
DateFormat dtF = new SimpleDateFormat("dd/MM/yyyy");
try {
date = dtF.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return Optional.ofNullable(date);
}

A good practice is to name the function with a more self-descriptive name like getDateFromString.

Is it ncessary to use Regex to check date sanity in addition to SimpleDateFormat.parse?

Define 'valid'.

The regex, for example, allows For the 39th day of the 19th month, as well as the 0th day of the 0th month: 39-19-0000 is a valid date according to it, but 1-1-2020 is not (only 01-01-2020 would be).

The SDF is old API that you shouldn't use (use java.time), but as far as this code goes, lenience is turned off, so it does not accept 39-19-0000, though it would accept 1-1-2020.

Combining the two gets you the strictest take, but it sure feels convoluted. Is there a particular heinous issue with accepting 1-1-2020?

But note this gigantic problem: Year confusion. 1-1-99 WILL parse with a .setLenient(false) SDF with pattern dd.MM.yyyy, and is interpreted as jan 1st during the year 99, sometime during the days of the roman empire (not like the prince song).

For that purpose alone, that regex is quite useful; you can use the regexp to error out if a 2-digit year is passed, as SDF can't reject that kind of input.

Thus, the real advice!

Those old date apis are so incredibly bad. They suck. Do not use them. Update your IDE, mark down SimpleDateFormat as illegal. Here is the java time package, doing precisely what you want. Note that SDF returns a java.util.Date which is a lying liar who lies: That represent an instant in time and has no business representing dates. It really doesn't - that's why all the various .getYear() etc methods are deprecated.

LocalDate x = LocalDate.parse("01.01.1999", DateTimeFormatter.ofPattern("dd.MM.yyyy"));
System.out.println(x);
> 1999-01-01

// Awesome. It's an actual _DATE_ and not a timestamp where timezones
// can mess everything up.

LocalDate x = LocalDate.parse("01.01.99", DateTimeFormatter.ofPattern("dd.MM.yyyy"));
> Exception in thread "main" java.time.format.DateTimeParseException: Text '01.01.1999' could not be parsed

// perfect. it crashes, as it should.

LocalDate x = LocalDate.parse("1.1.1999", DateTimeFormatter.ofPattern("dd.MM.yyyy"));
> Exception in thread "main" java.time.format.DateTimeParseException: Text '01.01.1999' could not be parsed

// if you want `1.1.1999` to crash, well, you can.

LocalDate x = LocalDate.parse("1.1.1999", DateTimeFormatter.ofPattern("d.M.yyyy"));
System.out.println(x);
> 1999-01-01

// but you don't have to. with just `d`, 1 parses as 1, so does 01.

You can go with yy too, which FORCES that you only use 2 digits; they are all interpreted as 20xx. (1.1.99 is 2099-01-01).

How can I show the numbers of valid dates in Java?

You can parse each date and its numbers in a try/catch block, and increment a counter as follows:

public static void main(String[] args) {
String[] dates = new String[]{"car","bench","01/04/2019", "01/13/2019", "29/02/200s"};
System.out.println(validate(dates));
}
private static int validate(String[] dates){
int count = 0;
DateFormat format = new SimpleDateFormat("dd/MM/yyyy");
format.setLenient(false);
for(String date : dates) {
try {
format.parse(date);
String[] dateParts = date.split("/");
for(String str : dateParts)
Integer.parseInt(str);
if(dateParts.length!=3 || dateParts[0].length()!=2 || dateParts[1].length()!=2 || dateParts[2].length()!=4)
throw new Exception();
count++;
} catch (Exception e) {
System.out.println("Date " + date + " is not valid");
}
}
return count;
}

Output:

Date car is not valid
Date bench is not valid
Date 01/13/2019 is not valid
Date 29/02/200s is not valid
1

EDIT :
According to Ole's comment and this post, it's better to use more accurate libraries:

public static void main(String[] args) {
String[] dates = new String[]{"car","bench","01/04/2019", "01/13/2019", "29/02/200s"};
System.out.println(validate(dates));
}
private static int validate(String[] dates){
int count = 0;
for(String date : dates) {
try {
String[] dateParts = date.split("/");
if(dateParts.length==3 && isDateValid(Integer.parseInt(dateParts[2]), Integer.parseInt(dateParts[1]), Integer.parseInt(dateParts[0])))
count++;
else
throw new Exception();
} catch (Exception e) {
System.out.println("Date " + date + " is not valid");
}
}
return count;
}
private static boolean isDateValid(int year, int month, int day) {
boolean dateIsValid = true;
try {
LocalDate.of(year, month, day);
} catch (DateTimeException e) {
dateIsValid = false;
}
return dateIsValid;
}

How can I check if my Date is correct?

Trying to parse a string like this: string = "36/03/2017"; can return a totally legal date object but an incorrect value respect the string.
for that case you can use the lenient flag of the class SimpleDateFormat, setting that flag to false will throw an exception for invalid strings representations of a date (e.g. 29 Feb 2017)

now, you can set the flag Lenient to false, and catch the exception

String string = "36/03/2017";
DateFormat format = new SimpleDateFormat("dd/MM/yyyy");
format.setLenient(false);
try {
Date date = format.parse(string);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}


Related Topics



Leave a reply



Submit