Reputation: 53
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.
Upvotes: 4
Views: 8288
Reputation: 86232
DateTimeFormatter
from java.time.!=
for comparing strings.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
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
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.
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.
Upvotes: 0
Reputation: 140427
A simple approach that probably is expensive; but somehow against good practices goes like this:
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:
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 formatUpvotes: 5
Reputation: 121649
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;
}
Upvotes: 6
Reputation: 827
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
}
Upvotes: 3