0

I'm using the following code to categorize whether or not the data type of text input is a date:

private boolean isDate(String val)
{
    //These are the only formats dates will have
    String[] formatList = {"MM/dd/yyyy", "MM-dd-yyyy", "MMM/dd/yyyy", "dd-MMM-yyyy"};
    SimpleDateFormat dateFormat = new SimpleDateFormat();
    dateFormat.setLenient(false);
    dateFormat.

    //Loop through formats in formatList, and if one matches the string return true
    for (String str:formatList)
    {
        try
        {
            dateFormat.applyPattern(str);
            dateFormat.parse(val.trim());
        } catch (ParseException pe)
        {
            continue;
        }
        return true;
    }
    return false;
}

This method is called in a switch statement along with other function like isNumber, isDatetime, isVarchar2, etc. When I pass the value '4/05/2013 23:54' which is a datetime, the SimpleDateFormat object successfully parses it. I assume this is because it performs a regex match. This means I have to call isDate after isDatetime or nothing will ever be categorized as a datetime. Is there a simple way to get SimpleDateFormat to strictly match (ie, choke when there are extra characters)?

YCF_L
  • 49,027
  • 13
  • 75
  • 115

2 Answers2

3

I would like to use java.time API from Java8+ with only one pattern like so :

private boolean isDate(String dateString) {
    DateTimeFormatter format =
            DateTimeFormatter.ofPattern(
                    "[M/d/uuuu][M-d-uuuu][MMM/d/uuuu][d-MMM-uuuu]"
            );
    try {
        LocalDate.parse(dateString, format);
        return true;
    }catch (DateTimeParseException e){
        return false;
    }
}

where [M/d/uuuu][M-d-uuuu][MMM/d/uuuu][d-MMM-uuuu] means to match either M/d/uuuu or M-d-uuuu or MMM/d/uuuu or d-MMM-uuuu

YCF_L
  • 49,027
  • 13
  • 75
  • 115
  • With `DateTimeFormatter`, you should use `uuuu` instead of `yyyy`. – Andreas Jul 13 '18 at 19:51
  • quick search I found your answer here https://stackoverflow.com/questions/41177442/uuuu-versus-yyyy-in-datetimeformatter-formatting-pattern-codes-in-java I don't know about that @Andreas thank you for the information I will edit my answer and +1 for the detailed answer – YCF_L Jul 13 '18 at 19:53
  • @Andreas IMHO that’s only a mild “should”. In most often do that, but strictly speaking it depends on requirements, and in 99 % of situations it doesn’t matter in practice.. If `yyyy` works as it should in the question, it does in this answer too. – Ole V.V. Jul 13 '18 at 19:54
  • Hi @OleV.V. your comments makes me wait and search more about that – YCF_L Jul 13 '18 at 19:56
  • 2
    I think you meant for `catch` block to be `return false;`, otherwise return type `boolean` makes little sense. Also, it seems that OP expects `4/05/2013` to return true, which happens with `SimpleDateFormat`, but doesn't happen with your code. `dd` only parses 2-digit values. Use `d` to parse 1-digit values, i.e. the pattern should be `"[M/d/uuuu][M-d-uuuu][MMM/d/uuuu][d-MMM-uuuu]"` – Andreas Jul 13 '18 at 20:00
2

If you read the documentation, i.e. the javadoc of parse, you'd see:

Parses text from the beginning of the given string to produce a date. The method may not use the entire text of the given string.

See the parse(String, ParsePosition) method for more information on date parsing.

The javadoc of the other method says:

Parameters:

source - The date/time string to be parsed

pos - On input, the position at which to start parsing; on output, the position at which parsing terminated, or the start position if the parse failed.

So, to ensure the entire text matches the date format, use the second method and check the ending parse position.

This also has the beneficial effect of not using exceptions for control flow.

private static boolean isDate(String val) {
    String trimmedVal = val.trim();
    
    //These are the only formats dates will have
    String[] formatList = {"MM/dd/yyyy", "MM-dd-yyyy", "MMM/dd/yyyy", "dd-MMM-yyyy"};
    SimpleDateFormat dateFormat = new SimpleDateFormat();
    dateFormat.setLenient(false);
    
    ParsePosition pos = new ParsePosition(0);
    for (String str : formatList) {
        pos.setIndex(0);
        dateFormat.applyPattern(str);
        dateFormat.parse(trimmedVal, pos);
        if (pos.getIndex() == trimmedVal.length())
            return true; // full text parsed without error
    }
    return false;
}

Test

System.out.println(isDate("12/31/2018"));
System.out.println(isDate("12-31-2018"));
System.out.println(isDate("Dec/31/2018"));
System.out.println(isDate("31-Dec-2018"));
System.out.println(isDate("4/05/2013"));
System.out.println(isDate("4/05/2013 23:54"));

Output

true
true
true
true
true
false
Community
  • 1
  • 1
Andreas
  • 138,167
  • 8
  • 112
  • 195