Main Tutorials

How to check if date is valid in Java

Java 8 date validator

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.

  1. Year format, 1900, 2099
  2. Month format, 1, 01, 2, 02… 12
  3. Day format, 1, 01… 31
  4. Leap year, February 29 days.
  5. Common year, February 28 days.
  6. Month 1, 3, 5, 7, 8, 10, 12, max 31 days.
  7. Month 4, 6, 9, 11, max 30 days.
  8. 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.

DateValidatorDateTimeFormatter.java

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.

DateValidatorTest.java

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.

unit tests all passed

3. SimpleDateFormat – yyyy-M-d

For legacy Java application, we use SimpleDateFormat and .setLenient(false) to validate a date format.

DateValidatorSimpleDateFormat.java

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.

  1. 2008-02-2x
  2. 20-11-02
  3. 12012-04-05

unit tests some failed

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

References

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
10 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
adithya kanduri
10 years ago

hey mykong..but 12012-04-05 will get failed (for yyyy-MM-dd format).Any idea how to avoid this

Ken
10 years ago

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.

Ahana
1 year ago
Reply to  mkyong

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

Gowtham
10 years ago

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.

Sander
10 years ago

setLenient(false) !!!!!!!!

thanks! that was just what I needed 🙂

Juan R.
10 years ago

Thank you so much!, just what I was looking for, simple as that!.

Cheers!

Zak
11 years ago

This is damn so simple solution… thanks a lot.