How to check if date is valid in Java
This article shows how to use the Java 8 DateTimeFormatter
to check if a date format is valid in Java.
Below are the requirements for a valid date.
- Year format, 1900, 2099
- Month format, 1, 01, 2, 02… 12
- Day format, 1, 01… 31
- Leap year, February 29 days.
- Common year, February 28 days.
- Month 1, 3, 5, 7, 8, 10, 12, max 31 days.
- Month 4, 6, 9, 11, max 30 days.
- ISO 8601, 2020-11-03,
yyyy-MM-dd
1. Date format – uuuu-M-d
In Java 8, we can use DateTimeFormatter
and ResolverStyle.STRICT to implement the above valid date requirements.
For DateTimeFormatter, the symbols y
means year-of-era, AD or BC; and the symbol u
means year, so we pick u
for the year format.
For month and day, we need to support the single or leading zero formats (1 or 01), we pick a single M
for the month, and single d
for the day. And the final date format is uuuu-M-d
.
Below is a Java 8 Date validator.
package com.mkyong.regex.date;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
public class DateValidatorDateTimeFormatter {
public static boolean isValid(final String date) {
boolean valid = false;
try {
// ResolverStyle.STRICT for 30, 31 days checking, and also leap year.
LocalDate.parse(date,
DateTimeFormatter.ofPattern("uuuu-M-d")
.withResolverStyle(ResolverStyle.STRICT)
);
valid = true;
} catch (DateTimeParseException e) {
e.printStackTrace();
valid = false;
}
return valid;
}
}
2. Date Validator Unit Tests.
Below is the unit tests for a list of valid and invalid date format.
package com.mkyong.regex.date;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class DateValidatorTest {
@ParameterizedTest(name = "#{index} - Run test with date = {0}")
@MethodSource("validDateProvider")
void test_date_java_api_valid(String date) {
assertTrue(DateValidatorDateTimeFormatter.isValid(date));
}
@ParameterizedTest(name = "#{index} - Run test with date = {0}")
@MethodSource("invalidDateProvider")
void test_date_java_api_invalid(String date) {
assertFalse(DateValidatorDateTimeFormatter.isValid(date));
}
static Stream<String> validDateProvider() {
return Stream.of(
"1998-09-30",
"1998-9-30",
"2020-09-1",
"2020-09-01",
"2020-9-1",
"2020-9-01",
"2020-2-29", // leap year
"2020-2-28", // leap year
"2019-2-28", // common year
"2000-02-29", // 2000 is a leap year, % 400 == 0
"1900-02-28", // 1900 is a common year
"2020-07-31",
"2020-08-31",
"2020-06-30",
"1900-01-01",
"2099-12-31");
}
static Stream<String> invalidDateProvider() {
return Stream.of(
"1998-09-31", // invalid day, sep max 30
"1998-11-31", // invalid day, nov max 30
"2008-02-2x", // invalid day 2x
"2008-0x-28", // invalid month 0x
"20xx-02-28", // invalid year 20xx
"20-11-02", // invalid year 20, must be yyyy
"2020/11/02", // invalid date format, yyyy-mm-dd
"2020-11-32", // invalid day, 32
"2020-13-30", // invalid month 13
"2020-A-20", // invalid month A
"2020-2-30", // leap year, feb max 29
"2019-2-29", // common year, feb max 28
"1900-02-29", // 1900 is a common year, feb max 28
"12012-04-05", // support only 4 digits years
" ", // empty
""); // empty
}
}
All passed.
3. SimpleDateFormat – yyyy-M-d
For legacy Java application, we use SimpleDateFormat
and .setLenient(false)
to validate a date format.
package com.mkyong.regex.date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class DateValidatorSimpleDateFormat {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-d");
public static boolean isValid(final String date) {
boolean valid = false;
try {
// why 2008-02-2x, 20-11-02, 12012-04-05 are valid date?
sdf.parse(date);
// strict mode - check 30 or 31 days, leap year
sdf.setLenient(false);
valid = true;
} catch (ParseException e) {
e.printStackTrace();
valid = false;
}
return valid;
}
}
Run the same #3 unit tests again; this time, we use the DateValidatorSimpleDateFormat
.
Three tests failed.
- 2008-02-2x
- 20-11-02
- 12012-04-05
Generally, SimpleDateFormat
can still validate most of the date format, except for some cases like the above three date formats.
Further Reading
If you don’t like the black box API magic to validate a date, consider this Java Regex to validate a date
Download Source Code
$ git clone https://github.com/mkyong/core-java
$ cd java-regex/date
hey mykong..but 12012-04-05 will get failed (for yyyy-MM-dd format).Any idea how to avoid this
Try Java 8 DateTimeFormatter
uuuu-M-d
, refer to example 1.You have a false assertion in testYearIsInvalid() as your month value is already wrong.
assertFalse(dateValidator.isThisDateValid(“31/20/19991”, “dd/MM/yyyy”));
SimpleDateFormat#parse allows years like 999999 even if they are not 4 characters.
The legacy
SimpleDateFormat
is not recommended to use. Try the new Java 8 DateTimeFormatter in strict mode.How to check if the file has correct date format (YYYYMMDD) and correct timestamp(SSHHMM) .If they are not in above format then assertion should fail
Isn’t there any other alternative to it because this seems to be an awkward practice (don’t take my wrong) because in any situation, we need to try our level best to prevent an exception to occur.
Thanks.
Try regex and code to validate a date.
https://mkyong.com/regular-expressions/how-to-validate-date-with-regular-expression/
setLenient(false) !!!!!!!!
thanks! that was just what I needed 🙂
Thank you so much!, just what I was looking for, simple as that!.
Cheers!
This is damn so simple solution… thanks a lot.