4

I am currently writing a validation method in Java to check if a string is in one of a few different format to be changed into a date.

The formats that I want it to accept are the following: MM/DD/YY , M/DD/YY, MM/D/YY, and M/D/YY.

I was testing the first format and every time it was telling me it was not valid even when I entered in a valid date.

Here is what my current code looks like:

public class IsDateFormatValid
{
   public boolean isValid(String date)
   {
      boolean result = true;
      if(date.length()>8||date.length()<6)
      {
         result= false;
      }
      if(date.length()==8)
      {
         if((Character.toString(date.charAt(2))!= "/")||(Character.toString(date.charAt(5))!="/"))
         {
            result=false;
         }   
      }
      if(date.length()==7)
      {
         if((Character.toString(date.charAt(2))!="/"&&Character.toString(date.charAt(1))!="/") ||(Character.toString(date.charAt(3))!="/"&&Character.toString(date.charAt(4))!= "/"))
         {
            result=false;
         }   
      }

      return result;   
   }
}   

I still need to put in the conditions for the last format case. I did a debug method and saw that the part that always returning false was the line that said: if((Character.toString(date.charAt(2))!= "/")||(Character.toString(date.charAt(5))!="/"))

The main point of this question is trying to check it against multiple formats not just a singular one how most other questions on here ask about.

4
  • 1
    Those questions makes me angry and very sad. If it is a date it should be a date. If it is not a date it should be null. Commented Aug 10, 2016 at 16:36
  • Good luck guessing what 1/2/06 means. :) Commented Aug 11, 2016 at 7:51
  • Very simple when you know it: LocalDate.parse(yourDateString, DateTimeFormatter.ofPattern("M/d/uu")) will accept all your four format variants and will throw a DateTimeParseException if the string is invalid. Commented Nov 7, 2019 at 20:55
  • For the record, this question was previously closed as a duplicate of Java: Check the date format of current string is according to required format or not [duplicate]. I have reopened. Commented Nov 9, 2019 at 2:16

4 Answers 4

6

You might want to iterate through possible formats, like this:

EXAMPLE:

private static String[] date_formats = {
        "yyyy-MM-dd",
        "yyyy/MM/dd",
        "dd/MM/yyyy",
        "dd-MM-yyyy",
        "yyyy MMM dd",
        "yyyy dd MMM",
        "dd MMM yyyy",
        "dd MMM yyyy"
};

/**
 * A brute-force workaround for Java's failure to accept "any arbitrary date format"
 */
public static Date tryDifferentFormats (String sDate) {
    Date myDate = null;
    for (String formatString : date_formats) {
        try {
            SimpleDateFormat format = new SimpleDateFormat(formatString);
            format.setLenient(false);
            myDate = format.parse(sDate);
            break;
        }
        catch (ParseException e) {
            // System.out.println("  fmt: " + formatString + ": FAIL");
        }
    }
    return myDate;
}
Sign up to request clarification or add additional context in comments.

2 Comments

@PeterRader Threading does not appear to be an issue here. The SimpleDateFormat format here is a method local variable.
The one problem with that: the really expensive part is the creation of that formatter object. And you keep doing that all the time. So, when you have m formats; and n incoming strings ... that gives you n*m new formatters. If m is large; boy, you are burning a lot of cpu cycles; and possible creating a lot of "garbage" here.
5

A simple approach that probably is expensive; but somehow against good practices goes like this:

  1. Create a list of Formatter objects (one for each allowed pattern).
  2. Iterate that list; and try if you can parse your date string using each formatter (with lenient set to false!). If you get one that doesn't throw an exception, you know that the incoming string conforms to a valid format.

For parsing with formats, you can checkout this question.

As Peter is pointing out, this solution isn't threadsafe. So you would need to look into that question to deal with that.

On the other hand, when doing it like paulsm4 suggests; you avoid the threading issue ... but unfortunately, you are then creating a lot of formatter objects; that you immediately throw away afterwards. Talk about wasting CPU cycles and creating "memory garbage" there.

Option 2; less "expensive" is to come up with one (or more) several regular expressions that would match strings of the given format. But of course, it isn't as easy as the one suggested from Susannah; as you know, you really would want to reject a string like "55/66/77" which perfectly matches a simple regex that just checks for "two digits dash two digits dash two digits".

So, yes, option 1 is expensive; so the question here is: how good should your validation be? Do you want to reject dates that are syntactically "correct", but that are "semantically" wrong, like "02/29/15" (2015 not being a leap year!)?!

Update: thinking about this, a nice solution goes like:

  1. Create a Map<Regex, String> where the value would be a string that can be used as "formatter input"; and the corresponding key is a regex that "matches" that format
  2. Iterate the map keys
  3. If no key matches: done; you know that your input has an unknown/invalid format
  4. If a key matches: fetch the map value for that key and use it to create a non-lenient formatter object. Now you can check if the formatter can parse your input. If so: input is a valid date in one of your formats.

Comments

3

Try matching against a regex, it will reduce the work you're doing.

if(date.matches("\\d{1-2}\\\\d{1-2}\\\\d{1-2}")){
    // ..do something
}

12 Comments

Keep in mind this will not check if the actual values are valid. You could easily pass 99/99/99 and it would say it's valid.
And the person is just trying to validate the slashes and length, not the actual content. The downvotes are thus unnecessary as this adequately accomplishes the task.
@SusannahPotts That isn't really clear. But your statement made me rethink my answer and come with "the best of both worlds"; a solution that uses a "cheap" approach to tell you if a date is "fully" valid.
@SusannahPotts Well, one could go really crazy on that. Like thinking about a "max length" for each format; and doing a simple length-check upfront; before applying any of the regexes. And you are correct, the name of function is actually giving a precise meaning.
@PeterRader I am Zen, I am peace. I am no fighting person. Ask the 10 newbies that I welcome here every day with close requests and downvotes.
|
0

TL;DR

  1. Your formats are (variants of) the same format.
  2. Use a formatter for validating whether a date string is valid.
    • In particular use DateTimeFormatter from java.time.
  3. Don’t use != for comparing strings.

They are variants of the same format

Unless I completely misunderstood, the variation of formats are that the month can be one or two digits and the day of month can be one or two digits. We can handle that as one single format. Like two other answers I believe that for most purposes it’s better to perform a full validation of whether we’ve got a valid date. Not just a validation of the number of digits and the positions of the slashes (which, as the answer by Susannah Potts says, a regex can do). So what we need to do is:

  1. Define a formatter
  2. Try to parse; if an exception is thrown, the string wasn’t a valid date string in any of your format variants.

1. Define a formatter

    DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("M/d/uu")
            .withResolverStyle(ResolverStyle.STRICT);

One pattern letter M will match a month number in either one or two digits. Similar for d for day of month.

Let’s see this in action first.

    System.out.println(LocalDate.parse("12/28/66", dateFormatter));
    System.out.println(LocalDate.parse("11/06/37", dateFormatter));
    System.out.println(LocalDate.parse("11/2/58", dateFormatter));
    System.out.println(LocalDate.parse("03/14/35", dateFormatter));
    System.out.println(LocalDate.parse("05/04/68", dateFormatter));
    System.out.println(LocalDate.parse("09/3/06", dateFormatter));
    System.out.println(LocalDate.parse("5/23/99", dateFormatter));
    System.out.println(LocalDate.parse("1/06/00", dateFormatter));
    System.out.println(LocalDate.parse("2/8/76", dateFormatter));

Output is:

2066-12-28
2037-11-06
2058-11-02
2035-03-14
2068-05-04
2006-09-03
2099-05-23
2000-01-06
2076-02-08

2.Try to parse and trap for exception

    String dateString = "12/28/66";
    try {
        LocalDate.parse(dateString, dateFormatter);
        System.out.println(dateString + " is valid");
    } catch (DateTimeParseException dtpe) {
        System.out.println(dateString + " is not valid: " + dtpe.getMessage());
    }
12/28/66 is valid

Try it with a date string that doesn’t follow any of the formats:

    String dateString = "ab/cd/ef";
ab/cd/ef is not valid: Text 'ab/cd/ef' could not be parsed at index 0

java.time

I am recommending java.time, the modern Java date and time API. The classes Date and SimpleDateFormat used in one answer are poorly designed and long outdated, the latter in particular notoriously troublesome. java.time is so much nicer to work with.

What went wrong in your code?

There are two problems.

One, as you said, this if condition always evaluates to true, which causes your result to be set to false:

         if((Character.toString(date.charAt(2))!= "/")||(Character.toString(date.charAt(5))!="/"))

Comparing strings with != doesn’t work the way you expect. This tests whether the two are the same string object, not whether the strings are unequal. Character.toString() creates a new string object, so this will always be another object than "/". So even though the string from Character.toString() contains a slash too, != will evaluate to true.

Two, you are not validating that the characters before, between and after the slashes are digits.

Links

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.