Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking the validity of a date [duplicate]

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.

like image 634
Bloodboiler Avatar asked Oct 22 '08 18:10

Bloodboiler


2 Answers

Key is df.setLenient(false);. This is more than enough for simple cases. If you are looking for a more robust (I doubt) and/or alternate libraries like joda-time then look at the answer by the user "tardate"

final static String DATE_FORMAT = "dd-MM-yyyy";  public static boolean isDateValid(String date)  {         try {             DateFormat df = new SimpleDateFormat(DATE_FORMAT);             df.setLenient(false);             df.parse(date);             return true;         } catch (ParseException e) {             return false;         } } 
like image 82
Aravind Yarram Avatar answered Oct 08 '22 14:10

Aravind Yarram


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.

  Date parseDate(String maybeDate, String format, boolean lenient) {     Date date = null;      // test date string matches format structure using regex     // - weed out illegal characters and enforce 4-digit year     // - create the regex based on the local format string     String reFormat = Pattern.compile("d+|M+").matcher(Matcher.quoteReplacement(format)).replaceAll("\\\\d{1,2}");     reFormat = Pattern.compile("y+").matcher(reFormat).replaceAll("\\\\d{4}");     if ( Pattern.compile(reFormat).matcher(maybeDate).matches() ) {        // date string matches format structure,        // - now test it can be converted to a valid date       SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance();       sdf.applyPattern(format);       sdf.setLenient(lenient);       try { date = sdf.parse(maybeDate); } catch (ParseException e) { }     }      return date;   }     // used like this:   Date date = parseDate( "21/5/2009", "d/M/yyyy", false); 

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:

Locale locale = Locale.getDefault(); SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale ); String format = sdf.toPattern(); 

Joda Time - Better Alternative?

I've been hearing about joda time recently and thought I'd compare. Two points:

  1. Seems better at being strict about invalid characters in the date string, unlike SimpleDateFormat
  2. Can't see a way to enforce 4-digit years with it yet (but I guess you could create your own DateTimeFormatter for this purpose)

It's quite simple to use:

import org.joda.time.format.*; import org.joda.time.DateTime;  org.joda.time.DateTime parseDate(String maybeDate, String format) {   org.joda.time.DateTime date = null;   try {     DateTimeFormatter fmt = DateTimeFormat.forPattern(format);     date =  fmt.parseDateTime(maybeDate);   } catch (Exception e) { }   return date; } 
like image 31
tardate Avatar answered Oct 08 '22 14:10

tardate