views:

81

answers:

3

In my project I need to check if a date string evaluates to a proper Date object. I've decided to allow yyyy-MM-dd, and Date formats [(year, month, date) and (year, month, date, hrs, min)]. How can I check if they're valid ? My code returns null for "1980-01-01" and some strange dates (like 3837.05.01) whon giving a string separated by commas :

private Date parseDate(String date){
    Date data = null;

    // yyy-mm-dd
    try {
        DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
        df.setLenient(false);
        df.parse(date);
        return data;
    }
    catch (Exception e) {
        try{
            int[] datArr = parseStringForDate(date);
            int len = datArr.length;
            // year, month, day
            if(len == 3){
                return new Date(datArr[0], datArr[1], datArr[2]);
            }
            // year, montd, day, hours, mins
            else if(len ==5){
                return new Date(datArr[0], datArr[1], datArr[2], datArr[3], datArr[4]);
            }
            // year, month, day, hours, mins, secs
            else if(len == 6){
                return new Date(datArr[0], datArr[1], datArr[2], datArr[3], datArr[4], datArr[5]);
            }
            else {
                return data;
            }
        }
        catch (Exception f){
            return data;
        }
    }
}

private int[] parseStringForDate(String s){
    String[] sArr = s.split(",");
    int[] dateArr = new int[sArr.length];

    for(int i=0; i< dateArr.length; i++){
        dateArr[i] = Integer.parseInt(sArr[i]);
    }

    return dateArr;
}

I remember that I had to subtract 1900 from year date, but I also see that month is different etc, and I'd like to avoid checking every element of my array of ints from date string. Is it possible to parse them automatically in Calendar or date object ?

+1  A: 

You are doing this the wrong way. You should use SimpleDateFormat.

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
    Date test = sdf.parse(input);
} catch (ParseException pe) {
   //Date is invalid, try next format
}
Vinko Vrsalovic
Okay, I see you've updated. But the OP is using some sort of three-digit year format, so this answer may not be useful.
Lord Torgamus
It doesn't return `null` on failure.
BalusC
If that's the case and not a typo, I guess it's still useful, you could split on the valid separators and prepend a 1 or a 2 depending on the read value, then apply the SimpleDateFormat to let it take care of leap years and whatnot.
Vinko Vrsalovic
that was a typo, sorry
dbnazz
+2  A: 

You can construct SimpleDateFormat objects for your different String formats like this (returning null if the parameter cannot be parsed as a valid date):

// Initializing possibleFormats somewhere only once
SimpleDateFormat[] possibleFormats = new SimpleDateFormat[] {
  new SimpleDateFormat("yyyy-MM-dd"),
  new SimpleDateFormat("yyyy,MM,dd"),
  new SimpleDateFormat("yyyy,MM,dd,HH,mm") };
for (SimpleDateFormat format: possibleFormats)
{
  format.setLenient(false);
}
// initializing ends

public Date parseDate(String date) {
  Date retVal = null;
  int index = 0;
  while (retVal == null && index < possibleFormats.length) {
    try {
      retVal = possibleFormats[index++].parse(date);
    } catch (ParseException ex) { /* Do nothing */ }
  }
  return retVal;
}
Csaba_H
BTW you can use a for loop (`for (Format f: possibleFormat) { ... }`) instead all that while loop logic.
Steve Kuo
Wow, thats a nice way to approach the problem. The only issue I see is that the OP talked about getting strange dates in 1980 and 3837 but you only check to see if the date is null.
TheLQ
this ends in a never ending loop if the ParseException is thrown - should increment outside the try-catch. this will also parse invalid dates as `2010-14-24` (returns `Feb. 24, 2011`) - SimpleDateFormat must be set to non-lenient.
Carlos Heuberger
@Steve: I don't like using return in the middle of a for, that's why I used a while. @Carlos: you are right, thanks, I have updated the code.
Csaba_H
You can terminate early out of a for loop using `break`.
Steve Kuo
@Steve: sure, but I need an extra if within the for so in my opinion using while is simpler and more 'understandable' in this case (all logic in one place). But, again, IMHO :)
Csaba_H
A: 

Just to show the final outcome, thanks to Csaba_H and Steve Kuo.

private Date parseDate(String date){

    SimpleDateFormat[] possibleFormats = new SimpleDateFormat[] {
        new SimpleDateFormat("yyyy-MM-dd"),
        new SimpleDateFormat("yyyy,MM,dd"),
        new SimpleDateFormat("yyyy,MM,dd,HH,mm") };

    Date retVal = null;
    for (SimpleDateFormat f: possibleFormats) {
        f.setLenient(false);
        try {
            retVal = f.parse(date);
        } catch (ParseException e) {}
    }
    return retVal;
}
dbnazz
I am glad to help. One last word: you should construct the possibleFormats only once (maybe statically if you are not in a multi-threaded context) to avoid constructing many 'throw-away' objects; and you should return after a successful parse in order to avoid the unnecessary exception creation and catching.
Csaba_H