My requirement is to validate that a date String is in the correct format based on a set of valid formats specified.
Valid formats:
MM/dd/yy
MM/dd/yyyy
I created a simple test method that uses the Java 8 DateTimeFormatterBuilder to create a flexible formatter that supports multiple optional formats. Here is the code:
public static void test() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy"))
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy"))
.toFormatter();
String dateString = "10/30/2017";
try {
LocalDate.parse(dateString, formatter);
System.out.println(dateString + " has a valid date format");
} catch (Exception e) {
System.out.println(dateString + " has an invalid date format");
}
}
When I run this, here is the output
10/30/2017 has an invalid date format
As you see in the code, the valid date formats are MM/dd/yy and MM/dd/yyyy.
My expectation was that the date 10/30/2017 should be valid as it matches MM/dd/yyyy. However, 10/30/2017 is being reported as invalid.
What is going wrong ? Why is this not working ?
I also tried
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy[yy]"))
in place of
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy"))
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy"))
but still had the same issue.
This code runs as expected if I use:
String dateString = "10/30/17";
in place of
String dateString = "10/30/2017";
I have 2 questions
What is going wrong here ? Why is it not working for "10/30/2017" ?
Using Java 8, how to correctly create a flexible Date formatter (a formatter that supports multiple optional formats) ? I know the use of [] to create optional sections in the pattern string itself. I'm looking for something more similar to what I am trying (avoiding [] inside the pattern string and using separate optional clauses for each separate format string)
The formatter does not work the way you expect, the optional part means
if there is nothing extra attached to the first pattern (e.g., "MM/dd/yy"), that is fine,
if there is something extra, it needs to match the second pattern (e.g, "MM/dd/yyyy")
To make it a bit clearer, try to run the sample code below to understand it better:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy"))
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy"))
.toFormatter();
String[] dateStrings = {
"10/30/17", // valid
"10/30/2017", // invalid
"10/30/1710/30/2017", // valid
"10/30/201710/30/17" // invalid
};
for (String dateString : dateStrings) {
try {
LocalDate.parse(dateString, formatter);
System.out.println(dateString + " has a valid date format");
} catch (Exception e) {
System.err.println(dateString + " has an invalid date format");
}
}
==
10/30/17 has a valid date format
10/30/1710/30/2017 has a valid date format
10/30/2017 has an invalid date format
10/30/201710/30/17 has an invalid date format
==
This is only a simple solution, if performance is of your concern, the validation by catching the parsing exception should be the last resort
you may check the string by length or regex first before doing the date string parsing
you may also replace the stream with a method containing a simple for loop, etc.
String[] patterns = { "MM/dd/yy", "MM/dd/yyyy" };
Map<String, DateTimeFormatter> formatters = Stream.of(patterns).collect(Collectors.toMap(
pattern -> pattern,
pattern -> new DateTimeFormatterBuilder().appendOptional(DateTimeFormatter.ofPattern(pattern)).toFormatter()
));
String dateString = "10/30/17";
boolean valid = formatters.entrySet().stream().anyMatch(entry -> {
// relying on catching parsing exception will have serious expense on performance
// a simple check will already improve a lot
if (dateString.length() == entry.getKey().length()) {
try {
LocalDate.parse(dateString, entry.getValue());
return true;
}
catch (DateTimeParseException e) {
// ignore or log it
}
}
return false;
});
The builder's appendValueReduced() method was designed to handle this case.
When parsing a complete value for a field, the formatter will treat it as an absolute value.
When parsing an partial value for a field, the formatter will interpret it relative to a base that you specify. For example, if you want two-digit years to be interpreted as being between 1970 and 2069, you can specify 1970 as your base. Here's an illustration:
LocalDate century = LocalDate.ofEpochDay(0); /* Beginning Jan. 1, 1970 */
DateTimeFormatter f = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ofPattern("MM/dd/"))
.appendValueReduced(ChronoField.YEAR, 2, 4, century)
.toFormatter();
System.out.println(LocalDate.parse("10/30/2017", f)); /* 2017-10-30 */
System.out.println(LocalDate.parse("10/30/17", f)); /* 2017-10-30 */
System.out.println(LocalDate.parse("12/28/1969", f)); /* 1969-12-28 */
System.out.println(LocalDate.parse("12/28/69", f)); /* 2069-12-28 */
Related
This question already has answers here:
I have a date in(string) in dd-mon-yyyy format and I want to compare this date with system date
(4 answers)
Closed 3 years ago.
I want to check if input string is valid date or not.
String be like :-
"08-Nov-2011"
"21 Mar 2019"
java code :-
boolean checkFormat;
String input = "08-Nov-2011";
if (input.matches("([0-9]{2})/([0-9]{2})/([0-9]{4})"))
checkFormat=true;
else
checkFormat=false;
System.out.println(checkFormat);
I am thinking of splitting and then check by its length like if first split word be of length 2, second split word be of length 3 and last word be of length 4.
But if Input String be like :-
AB-000-MN89
Then here it will fails.
Please help me to Solve this.
As stated in several comments, the best way to find out if your date is valid is to try to parse it with a java.time.format.DateTimeFormatter to a date object of type LocalDate.
You can support several patterns and/or use built-in ones from the DateTimeFormatter class:
public static void main(String[] args) {
// provide some patterns to be supported (NOTE: there are also built-in patterns!)
List<String> supportedPatterns = new ArrayList<>();
supportedPatterns.add("dd.MMM.yyyy");
supportedPatterns.add("dd MMM yyyy");
supportedPatterns.add("dd-MMM-yyyy");
supportedPatterns.add("dd/MMM/yyyy");
supportedPatterns.add("ddMMMyyyy");
// define some test input
String input = "08-Nov-2011";
// provide a variable for each, pattern and the date
String patternThatWorked = null;
LocalDate output = null;
// try to parse the input with the supported patterns
for (String pattern : supportedPatterns) {
try {
output = LocalDate.parse(input, DateTimeFormatter.ofPattern(pattern));
// until it worked (the line above this comment did not throw an Exception)
patternThatWorked = pattern; // store the pattern that "made your day" and exit the loop
break;
} catch (DateTimeParseException e) {
// no need for anything here but telling the loop to do the next try
continue;
}
}
// check if the parsing was successful (output must have a value)
if (output != null) {
System.out.println("Successfully parsed " + input
+ " to " + output.format(DateTimeFormatter.ISO_LOCAL_DATE) // BUILT-IN pattern!
+ " having used the pattern " + patternThatWorked);
}
}
This outputs
Successfully parsed 08-Nov-2011 to 2011-11-08 having used the pattern dd-MMM-yyyy
You can use SimpleDateFormat with the following pattern: dd-MMM-yyyy. Follow the link to see possible patterns.
SimpleDateFormat may throw ParseException where argument is invalid. So, you can wrap that invocation with a try-catch block.
As an example:
private final String pattern = "dd-MMM-yyyy";
private final SimpleDateFormat sdf = new SimpleDateFormat(pattern);
public boolean validateDate(String date) {
try {
sdf.parse(date);
return true;
} catch (ParseException e) {
return false;
}
}
If you have different formats as 08-Nov-2011 and 08 Nov 2011, try to unify them (by removing dashes from the first one, for instance).
A very crude regex for this would be:
\d{2}[- ]\w{3}[- ]\d{4}
08-Nov-2011
21 Mar 2019
Example here: https://regex101.com/r/01vslq/1.
However, it would probably be better to get a regex that don't allow dates like 99-Nov-9999, and so you could try a more thorough one here. However, even better, probably using Java date parsing -- this isn't a great use case for regex if you need to do legitimate date parsing -- for example, February shouldn't allow the numbers 29, 30, 31, and lots of other nuances. Use java.time.DateTimeFormatter (mentioned in the comment above).
I'm writing an application to manipulate text data, Which will change the content of input string and create new output String based on the format of input string.
I encounter some problem with recognized the date time string. Based on the document the input date time may have some optional section, here the sample pattern:
yyyy[MM[dd[HHmm]]][Z]
So after some digging on the web, my first attempt to use the parseBest function.
public boolean checkFormatDate(string input){
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy[MM[dd[HHmm]]][Z]");
try {
TemporalAccessor temporalAccessor = formatter.parseBest(input, ZonedDateTime::from, LocalDateTime::from, LocalDate::from);
return true;
} catch (Exception e) {
return false;
}
}
But the code above failed with these case:
1900
190001
190001011440
My suspect is that the queries that parse the parseBest method is not correct.
Can someone help me with this.
Edit:
Here is the exception log:
java.time.format.DateTimeParseException: Text '190001011440' could not be parsed at index 0
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1947)
at java.time.format.DateTimeFormatter.parseBest(DateTimeFormatter.java:1895)
The problem seems to be the pattern yyyy, which is creating a formatter as follows (System.out.println(formatter)):
Value(YearOfEra,4,19,EXCEEDS_PAD)[Value(MonthOfYear,2)[Value(DayOfMonth,2)[Value(HourOfDay,2)Value(MinuteOfHour,2)]]][Offset(+HHMM,'+0000')]
Note the 4,19 in the first part - minimum width of 4 and max of 19. Build the formatter as follows and it should work:
DateTimeFormatterBuilder b = new DateTimeFormatterBuilder();
formatter = b.appendValue(ChronoField.YEAR_OF_ERA, 4, 4, SignStyle.EXCEEDS_PAD).appendPattern("[MM[dd[HHmm]]][Z]").toFormatter();
I am using NetBeans IDE 7.2.
I have two separate classes newDateTest.java and newDateMethod.java, I am currently using my method class to validate a date from a user input which I have used in my test class.
So far in my test class I have the following:
try
{
Prompt ="please enter a date in the format dd-mm-yyyy";
System.out.println(Prompt);
String inputDate = in.next();
isValid = newDateMethod.validDate(input, input, input);
if (isValid){
System.out.println("VALID DATE");
} else {
System.out.println("INVALID DATE");
}
} catch (ArrayIndexOutOfBoundsException oob) {
System.out.println(oob);
}
However I have no idea how to validate the date in my method class as I am fairly new to Java.
Can anyone come to a solution? The sort of thing I've been taught to use is Date Formatter but I'm not sure whether this is appropriate here? If so, I wouldn't know how to use it
Like this:
Date date = null;
String inputDate = "07-01-2013";
try {
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
formatter.setLenient(false);
date = formatter.parse(inputDate);
} catch (ParseException e) {
e.printStackTrace();
}
Updated on 13-Jul-2021:
I heartily agree with Ole V.V.'s comment below. All Java and Kotlin developers should prefer the java.time package.
I'll add a more modern example when time permits.
Have a look at SimpleDateFormat.parse(...) and do remember to surround with try-catch.
The standard JDK class for that is SimpleDateFormat:
DateFormat fmt = new SimpleDateFormat("yourformathere");
// use fmt.parse() to check for validity
Alternatively, I'd recommend using Joda Time's DateTimeFormat.
Rather than relying on exceptions which tend to have a small performance overhead, you can also use the DateValidator from the Apache commons routines package like this:
if (DateValidator.getInstance().validate(inputDate, "dd-MM-yyyy") != null) {
// Date is valid
}
else {
// Date is invalid
}
Small disclaimer though, I haven't looked at the implementation of the validate method and I'm not sure if it uses for instance the SimpleDateFormat...
java.time
I recommend that you use java.time, the modern Java date and time API, for your date work. It also gives you much preciser validation than the old SimpleDateFormat class used in some of the other answers.
String[] exampleInputStrings = { "07-01-2013", "07-01-017",
"07-01-2ooo", "32-01-2017", "7-1-2013", "07-01-2013 blabla" };
for (String inputDate : exampleInputStrings) {
try {
LocalDate date = LocalDate.parse(inputDate, DATE_FORMATTER);
System.out.println(inputDate + ": valid date: " + date );
} catch (DateTimeParseException dtpe) {
System.out.println(inputDate + ": invalid date: " + dtpe.getMessage());
}
}
Output from my example code is:
07-01-2013: valid date: 2013-01-07
07-01-017: invalid date: Text '07-01-017' could not be parsed at index 6
07-01-2ooo: invalid date: Text '07-01-2ooo' could not be parsed at index 6
32-01-2017: invalid date: Text '32-01-2017' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32
7-1-2013: invalid date: Text '7-1-2013' could not be parsed at index 0
07-01-2013 blabla: invalid date: Text '07-01-2013 blabla' could not be parsed, unparsed text found at index 10
For a good validation you should probably add a range check. Use the isBefore and/or the isAfter method of LocalDate.
Also if you are going to do anything with the date more than validating it, you should keep the LocalDate from the parsing around in your program (not the string).
Link
Oracle tutorial: Date Time explaining how to use java.time.
You should use SimpleDateFormat.parse(String) method. if the passed date is of wrong format it throws an exception in which case you return false.
public boolean validateDate(String date) {
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
try {
sdf.parse(date);
return true;
}
catch(ParseException ex) {
return false;
}
}
One can use joda-time.
DateTimeFormat.forPattern(INPUTED_DATE_FORMAT);
//one can also use it with locale
DateTimeFormat.forPattern(USER_DATE_FORMAT).withLocale(locale);
fmt.parseDateTime(INPUTED_DATE);
If parseDateTime throw IllegalArgumentException then date is not valid.
I need to Validate Date in a specified format where both the inputs will be given only in the runtime in JTextfield and will be changing dynamically. Below is the code I have tried:
Date dd = new Date();
DateFormat df = new SimpleDateFormat(Date_format_text.getText());
try {
df.setLenient(false);
Date d1 = df.parse(Lower_date_text.getText());
System.out.println("Correct");
validator_LD.setVisible(false);
} catch (ParseException p) {
validator_LD.setText("*Not in mentioned Format '" + df.format(dd) + "'");
validator_LD.setVisible(true);
System.out.println("Wrong");
}
The above is.. i get the Date specified and the format specified from the text field and try to parse according to the specified format. If it doesn't match it will throw exception.
But this is not working properly in some cases :
If I give the Date 02/01/20'and the Format - dd/MM/YYYY where it should throw an exception since I have given the year as 20 and the format is 'YYYY' but i doesn't give exception.
Kindly help me.. Thanks in advance
First, you may want to take a look at How to Use the Focus Subsystem, paying attention to Validating Input which might help.
Second, as pointed out by #eatSleepCode, you're not actually parsing the text of the field, but are simply formatting an existing Date, so it will never throw an exception...
simple_format = new SimpleDateFormat(Date_format_text.getText());
// This is simply formatting the dates...
String ss = simple_format.format(dates);
Instead, you need to use something more like...
String test = "02/01/20";
String format = "dd/MM/yyyy";
SimpleDateFormat sdf = new SimpleDateFormat(format);
sdf.setLenient(false);
try {
Date date = sdf.parse(test);
if (!sdf.format(date).equals(test)) {
throw new ParseException(test + " is not a valid format for " + format, 0);
}
} catch (ParseException ex) {
ex.printStackTrace();
}
What this does, is test's the parser capabilities of the formatter, but also checks the input against what the resulting parsed Date would be formatted to, if these don't match it throws a ParseException. This is the closes I've been able to get to a strict parser...
Also, YYYY used to represent the week in year, not the year...
I am using NetBeans IDE 7.2.
I have two separate classes newDateTest.java and newDateMethod.java, I am currently using my method class to validate a date from a user input which I have used in my test class.
So far in my test class I have the following:
try
{
Prompt ="please enter a date in the format dd-mm-yyyy";
System.out.println(Prompt);
String inputDate = in.next();
isValid = newDateMethod.validDate(input, input, input);
if (isValid){
System.out.println("VALID DATE");
} else {
System.out.println("INVALID DATE");
}
} catch (ArrayIndexOutOfBoundsException oob) {
System.out.println(oob);
}
However I have no idea how to validate the date in my method class as I am fairly new to Java.
Can anyone come to a solution? The sort of thing I've been taught to use is Date Formatter but I'm not sure whether this is appropriate here? If so, I wouldn't know how to use it
Like this:
Date date = null;
String inputDate = "07-01-2013";
try {
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy");
formatter.setLenient(false);
date = formatter.parse(inputDate);
} catch (ParseException e) {
e.printStackTrace();
}
Updated on 13-Jul-2021:
I heartily agree with Ole V.V.'s comment below. All Java and Kotlin developers should prefer the java.time package.
I'll add a more modern example when time permits.
Have a look at SimpleDateFormat.parse(...) and do remember to surround with try-catch.
The standard JDK class for that is SimpleDateFormat:
DateFormat fmt = new SimpleDateFormat("yourformathere");
// use fmt.parse() to check for validity
Alternatively, I'd recommend using Joda Time's DateTimeFormat.
Rather than relying on exceptions which tend to have a small performance overhead, you can also use the DateValidator from the Apache commons routines package like this:
if (DateValidator.getInstance().validate(inputDate, "dd-MM-yyyy") != null) {
// Date is valid
}
else {
// Date is invalid
}
Small disclaimer though, I haven't looked at the implementation of the validate method and I'm not sure if it uses for instance the SimpleDateFormat...
java.time
I recommend that you use java.time, the modern Java date and time API, for your date work. It also gives you much preciser validation than the old SimpleDateFormat class used in some of the other answers.
String[] exampleInputStrings = { "07-01-2013", "07-01-017",
"07-01-2ooo", "32-01-2017", "7-1-2013", "07-01-2013 blabla" };
for (String inputDate : exampleInputStrings) {
try {
LocalDate date = LocalDate.parse(inputDate, DATE_FORMATTER);
System.out.println(inputDate + ": valid date: " + date );
} catch (DateTimeParseException dtpe) {
System.out.println(inputDate + ": invalid date: " + dtpe.getMessage());
}
}
Output from my example code is:
07-01-2013: valid date: 2013-01-07
07-01-017: invalid date: Text '07-01-017' could not be parsed at index 6
07-01-2ooo: invalid date: Text '07-01-2ooo' could not be parsed at index 6
32-01-2017: invalid date: Text '32-01-2017' could not be parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32
7-1-2013: invalid date: Text '7-1-2013' could not be parsed at index 0
07-01-2013 blabla: invalid date: Text '07-01-2013 blabla' could not be parsed, unparsed text found at index 10
For a good validation you should probably add a range check. Use the isBefore and/or the isAfter method of LocalDate.
Also if you are going to do anything with the date more than validating it, you should keep the LocalDate from the parsing around in your program (not the string).
Link
Oracle tutorial: Date Time explaining how to use java.time.
You should use SimpleDateFormat.parse(String) method. if the passed date is of wrong format it throws an exception in which case you return false.
public boolean validateDate(String date) {
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");
try {
sdf.parse(date);
return true;
}
catch(ParseException ex) {
return false;
}
}
One can use joda-time.
DateTimeFormat.forPattern(INPUTED_DATE_FORMAT);
//one can also use it with locale
DateTimeFormat.forPattern(USER_DATE_FORMAT).withLocale(locale);
fmt.parseDateTime(INPUTED_DATE);
If parseDateTime throw IllegalArgumentException then date is not valid.