93

I find it curious that the most obvious way to create Date objects in Java has been deprecated and appears to have been "substituted" with a not so obvious to use lenient calendar.

How do you check that a date, given as a combination of day, month, and year, is a valid date?

For instance, 2008-02-31 (as in yyyy-mm-dd) would be an invalid date.

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Bloodboiler
  • 2,082
  • 2
  • 24
  • 21
  • For anyone with a similar question, please consider whether you need non-Gregorian calendar support. – MSalters Apr 12 '10 at 14:31

25 Answers25

91

Key is df.setLenient(false);. This is more than enough for simple cases. If you are looking for a more robust (I doubt that) and/or alternate libraries like joda-time, then look at the answer by user "tardate"

final static String DATE_FORMAT = "dd-MM-yyyy";

public static boolean isDateValid(String date)
{
        try {
            DateFormat df = new SimpleDateFormat(DATE_FORMAT);
            df.setLenient(false);
            df.parse(date);
            return true;
        } catch (ParseException e) {
            return false;
        }
}
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Aravind Yarram
  • 78,777
  • 46
  • 231
  • 327
  • 3
    Try with "09-04-201a". It will create a crazy date. – ceklock Apr 10 '14 at 00:44
  • @ceklock that's the way it works, no matter if you use `setLenient` or not: `SimpleDateFormat` will always parse until the pattern is matched and ignore the rest of the string, thus you get `201` as year. – Daniel Naber Jun 04 '14 at 13:48
  • @ceklock I just addressed it in [my solution](http://stackoverflow.com/a/30528952/1276636). It may save a minute or two for someone. – Sufian May 29 '15 at 11:56
  • 1
    Incorporating exception handling entails a big performance hit, so this is probably a bad design IF you expect malformed input in normal operation (e.g. validating user input). But if the method is used as a double-check against inputs that are supposed to be valid all the time (except for bugs), it is fine. – RavenMan Sep 04 '15 at 19:59
  • 2
    FYI, the terribly troublesome old date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/10/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/10/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes built into Java 8 and later. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Aug 11 '18 at 18:37
49

As shown by @Maglob, the basic approach is to test the conversion from string to date using SimpleDateFormat.parse. That will catch invalid day/month combinations like 2008-02-31.

However, in practice that is rarely enough since SimpleDateFormat.parse is exceedingly liberal. There are two behaviours you might be concerned with:

Invalid characters in the date string Surprisingly, 2008-02-2x will "pass" as a valid date with locale format = "yyyy-MM-dd" for example. Even when isLenient==false.

Years: 2, 3 or 4 digits? You may also want to enforce 4-digit years rather than allowing the default SimpleDateFormat behaviour (which will interpret "12-02-31" differently depending on whether your format was "yyyy-MM-dd" or "yy-MM-dd")

A Strict Solution with the Standard Library

So a complete string to date test could look like this: a combination of regex match, and then a forced date conversion. The trick with the regex is to make it locale-friendly.

  Date parseDate(String maybeDate, String format, boolean lenient) {
    Date date = null;

    // test date string matches format structure using regex
    // - weed out illegal characters and enforce 4-digit year
    // - create the regex based on the local format string
    String reFormat = Pattern.compile("d+|M+").matcher(Matcher.quoteReplacement(format)).replaceAll("\\\\d{1,2}");
    reFormat = Pattern.compile("y+").matcher(reFormat).replaceAll("\\\\d{4}");
    if ( Pattern.compile(reFormat).matcher(maybeDate).matches() ) {

      // date string matches format structure, 
      // - now test it can be converted to a valid date
      SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance();
      sdf.applyPattern(format);
      sdf.setLenient(lenient);
      try { date = sdf.parse(maybeDate); } catch (ParseException e) { }
    } 
    return date;
  } 

  // used like this:
  Date date = parseDate( "21/5/2009", "d/M/yyyy", false);

Note that the regex assumes the format string contains only day, month, year, and separator characters. Aside from that, format can be in any locale format: "d/MM/yy", "yyyy-MM-dd", and so on. The format string for the current locale could be obtained like this:

Locale locale = Locale.getDefault();
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale );
String format = sdf.toPattern();

Joda Time - Better Alternative?

I've been hearing about joda time recently and thought I'd compare. Two points:

  1. Seems better at being strict about invalid characters in the date string, unlike SimpleDateFormat
  2. Can't see a way to enforce 4-digit years with it yet (but I guess you could create your own DateTimeFormatter for this purpose)

It's quite simple to use:

import org.joda.time.format.*;
import org.joda.time.DateTime;

org.joda.time.DateTime parseDate(String maybeDate, String format) {
  org.joda.time.DateTime date = null;
  try {
    DateTimeFormatter fmt = DateTimeFormat.forPattern(format);
    date =  fmt.parseDateTime(maybeDate);
  } catch (Exception e) { }
  return date;
}
tardate
  • 16,424
  • 15
  • 50
  • 50
  • I ended up with the joda alternative and checking that the value matches the pattern length myself... – Aritz Aug 04 '16 at 15:43
  • Update: The terrible old legacy classes (`Date`, `SimpleDateFormat`, etc.) are now supplanted by the modern *java.time* classes. Likewise, the *Joda-Time* project is in maintenance mode, and advises migration to the *java.time* classes. – Basil Bourque Aug 11 '18 at 18:31
42

tl;dr

Use the strict mode on java.time.DateTimeFormatter to parse a LocalDate. Trap for the DateTimeParseException.

LocalDate.parse(                   // Represent a date-only value, without time-of-day and without time zone.
    "31/02/2000" ,                 // Input string.
    DateTimeFormatter              // Define a formatting pattern to match your input string.
    .ofPattern ( "dd/MM/uuuu" )
    .withResolverStyle ( ResolverStyle.STRICT )  // Specify leniency in tolerating questionable inputs.
)

After parsing, you might check for reasonable value. For example, a birth date within last one hundred years.

birthDate.isAfter( LocalDate.now().minusYears( 100 ) )

Avoid legacy date-time classes

Avoid using the troublesome old date-time classes shipped with the earliest versions of Java. Now supplanted by the java.time classes.

LocalDate & DateTimeFormatter & ResolverStyle

The LocalDate class represents a date-only value without time-of-day and without time zone.

String input = "31/02/2000";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu" );
try {
    LocalDate ld = LocalDate.parse ( input , f );
    System.out.println ( "ld: " + ld );
} catch ( DateTimeParseException e ) {
    System.out.println ( "ERROR: " + e );
}

The java.time.DateTimeFormatter class can be set to parse strings with any of three leniency modes defined in the ResolverStyle enum. We insert a line into the above code to try each of the modes.

f = f.withResolverStyle ( ResolverStyle.LENIENT );

The results:

  • ResolverStyle.LENIENT
    ld: 2000-03-02
  • ResolverStyle.SMART
    ld: 2000-02-29
  • ResolverStyle.STRICT
    ERROR: java.time.format.DateTimeParseException: Text '31/02/2000' could not be parsed: Invalid date 'FEBRUARY 31'

We can see that in ResolverStyle.LENIENT mode, the invalid date is moved forward an equivalent number of days. In ResolverStyle.SMART mode (the default), a logical decision is made to keep the date within the month and going with the last possible day of the month, Feb 29 in a leap year, as there is no 31st day in that month. The ResolverStyle.STRICT mode throws an exception complaining that there is no such date.

All three of these are reasonable depending on your business problem and policies. Sounds like in your case you want the strict mode to reject the invalid date rather than adjust it.


Table of all date-time types in Java, both modern and legacy.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

Table of which java.time library to use with which version of Java or Android

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Well, assume that the project is being compiled with java 8+, then your answer is correct. I'm using these classes too when the project is in java 8. But sometimes I have to touch those ugly ancient jsp scriptlets (that even doesn't support java 7). Validating dates there is a pain. Hence I'm here. Yet your answer is the most correct approach... Conclusion: I am screwed. – KarelG Apr 19 '17 at 11:37
  • @KarelG Re-read that second-to-last paragraph about [back-port to Java 6 & Java 7](http://www.threeten.org/threetenbp/). I have not verified this behavior in the back-port but I suggest you give it a try. – Basil Bourque Apr 19 '17 at 11:44
40

You can use SimpleDateFormat

For example something like:

boolean isLegalDate(String s) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setLenient(false);
    return sdf.parse(s, new ParsePosition(0)) != null;
}
Maglob
  • 1,583
  • 9
  • 11
  • 2
    One of the issues with this approach is that its gonna accept 0003-0002-001. – despot Aug 15 '13 at 09:15
  • 1
    Another issue is that setting 13 in the month returns a Date where the month is 01 the following year. – 8bitjunkie Dec 13 '13 at 13:34
  • FYI, the terribly troublesome old date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/10/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/10/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes built into Java 8 and later. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Aug 11 '18 at 18:37
39

The current way is to use the calendar class. It has the setLenient method that will validate the date and throw and exception if it is out of range as in your example.

Forgot to add: If you get a calendar instance and set the time using your date, this is how you get the validation.

Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
    cal.getTime();
}
catch (Exception e) {
  System.out.println("Invalid date");
}
Mateus Viccari
  • 7,389
  • 14
  • 65
  • 101
AdamC
  • 16,087
  • 8
  • 51
  • 67
  • 3
    don't think this exactly works as-is. Calendar.setTime takes java.util.Date, so the conversion from string has already happened by the time you get a "yourDate" object. – tardate May 21 '09 at 10:08
  • 35
    3 problems with the example code: 1. After getting a Calendar instance, you must call cal.setLenient(false). Otherwise a date like Feb 31 2007 will be considered valid. 2. An exception doesn't get thrown by cal.setTime(). You must call cal.getTime() after the setTime() call, which throws an exception on invalid date. 3. Typo: Missing a '}' before the catch. – Liron Yahdav Apr 25 '10 at 01:14
  • This is also slower than other approaches. See http://stackoverflow.com/questions/2149680/regex-date-format-validation-on-java/18252071#18252071 – despot Aug 15 '13 at 11:59
  • 6
    FYI, the troublesome old date-time classes such as [`java.util.Date`](https://docs.oracle.com/javase/10/docs/api/java/util/Date.html), [`java.util.Calendar`](https://docs.oracle.com/javase/10/docs/api/java/util/Calendar.html), and `java.text.SimpleDateFormat` are now [legacy](https://en.wikipedia.org/wiki/Legacy_system), supplanted by the [*java.time*](https://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes built into Java 8 and later. See [*Tutorial* by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Apr 07 '18 at 23:50
  • 4
    doesn't validate dates like 31 Feb – shikha singh Jul 29 '19 at 10:37
16

java.time

With the Date and Time API (java.time classes) built into Java 8 and later, you can use the LocalDate class.

public static boolean isDateValid(int year, int month, int day) {
    try {
        LocalDate.of(year, month, day);
    } catch (DateTimeException e) {
        return false;
    }
    return true;
}
Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
  • 2
    By default, this code uses `ResolverStyle.SMART` which adjusts the resulting value to a valid date rather than throwing an exception. So this code will not accomplish the goal of the Question. See [my Answer](http://stackoverflow.com/a/39649815/642706) for example and for solution using `ResolverStyle.STRICT`. – Basil Bourque Sep 22 '16 at 22:30
  • @BasilBourqueno no it doesn't. Just tested and `LocalDate.of()` works just like `DateTimeFormatter` with `.withResolverStyle(ResolverStyle.STRICT)`. – Det Feb 02 '22 at 12:14
8

Building on Aravind's answer to fix the problem pointed out by ceklock in his comment, I added a method to verify that the dateString doesn't contain any invalid character.

Here is how I do:

private boolean isDateCorrect(String dateString) {
    try {
        Date date = mDateFormatter.parse(dateString);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return matchesOurDatePattern(dateString);    //added my method
    }
    catch (ParseException e) {
        return false;
    }
}

/**
 * This will check if the provided string matches our date format
 * @param dateString
 * @return true if the passed string matches format 2014-1-15 (YYYY-MM-dd)
 */
private boolean matchesDatePattern(String dateString) {
    return dateString.matches("^\\d+\\-\\d+\\-\\d+");
}
Sufian
  • 6,405
  • 16
  • 66
  • 120
7

An alternative strict solution using the standard library is to perform the following:

1) Create a strict SimpleDateFormat using your pattern

2) Attempt to parse the user entered value using the format object

3) If successful, reformat the Date resulting from (2) using the same date format (from (1))

4) Compare the reformatted date against the original, user-entered value. If they're equal then the value entered strictly matches your pattern.

This way, you don't need to create complex regular expressions - in my case I needed to support all of SimpleDateFormat's pattern syntax, rather than be limited to certain types like just days, months and years.

Ben
  • 71
  • 1
  • 1
  • This is definitely the way to go, see also sample code on http://www.dreamincode.net/forums/topic/14886-date-validation-using-simpledateformat/ – Victor Ionescu Jun 13 '12 at 13:30
5

I suggest you to use org.apache.commons.validator.GenericValidator class from apache.

GenericValidator.isDate(String value, String datePattern, boolean strict);

Note: strict - Whether or not to have an exact match of the datePattern.

Ziya
  • 720
  • 12
  • 18
  • I have added the answer with the method of GenericValidator: https://stackoverflow.com/a/68069005/2024527 – SANAT Jun 21 '21 at 13:41
5

I think the simpliest is just to convert a string into a date object and convert it back to a string. The given date string is fine if both strings still match.

public boolean isDateValid(String dateString, String pattern)
{   
    try
    {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        if (sdf.format(sdf.parse(dateString)).equals(dateString))
            return true;
    }
    catch (ParseException pe) {}

    return false;
}
mawa
  • 51
  • 1
  • 1
2

This is working great for me. Approach suggested above by Ben.

private static boolean isDateValid(String s) {
    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
    try {
        Date d = asDate(s);
        if (sdf.format(d).equals(s)) {
            return true;
        } else {
            return false;
        }
    } catch (ParseException e) {
        return false;
    }
}
2

Assuming that both of those are Strings (otherwise they'd already be valid Dates), here's one way:

package cruft;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateValidator
{
    private static final DateFormat DEFAULT_FORMATTER;

    static
    {
        DEFAULT_FORMATTER = new SimpleDateFormat("dd-MM-yyyy");
        DEFAULT_FORMATTER.setLenient(false);
    }

    public static void main(String[] args)
    {
        for (String dateString : args)
        {
            try
            {
                System.out.println("arg: " + dateString + " date: " + convertDateString(dateString));
            }
            catch (ParseException e)
            {
                System.out.println("could not parse " + dateString);
            }
        }
    }

    public static Date convertDateString(String dateString) throws ParseException
    {
        return DEFAULT_FORMATTER.parse(dateString);
    }
}

Here's the output I get:

java cruft.DateValidator 32-11-2010 31-02-2010 04-01-2011
could not parse 32-11-2010
could not parse 31-02-2010
arg: 04-01-2011 date: Tue Jan 04 00:00:00 EST 2011

Process finished with exit code 0

As you can see, it does handle both of your cases nicely.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • @duffymo the second statement isn't throwing exception... :( – maximus Dec 24 '10 at 19:52
  • @Pangea Not really.... try this date "31-01-2010" it won't throw any exception..........Just run it on your machine and see... it doesn't work.... – sasidhar Dec 24 '10 at 20:01
  • My bad. I removed my comment. But the second statement is throwing exception for me though. @duffymo - which jvm are you using? – Aravind Yarram Dec 24 '10 at 20:08
  • @Pangea i am using jdk1.6.0_23 I tried this with this even http://joda-time.sourceforge.net/ but even that didn't work.... – sasidhar Dec 24 '10 at 20:13
  • What did not work? Try doing a sys out of date 2 and post what is being printed here. I tried it with 1.6.0_20-b02. – Aravind Yarram Dec 24 '10 at 20:33
  • Exception in thread "main" java.text.ParseException: Unparseable date: "31-02-2010" at java.text.DateFormat.parse(DateFormat.java:337) – Aravind Yarram Dec 24 '10 at 20:39
  • @maximus: that's because the first has thrown an exception and thus the remnant of the code won't be executed. @sasidhar: that's because 31 january is a valid date. @duffymo: consider putting each in a try-catch so that both get executed ;) – BalusC Dec 24 '10 at 21:30
  • 1
    I understand this is just a simple test but to just to make sure that people doesn't copy this verbatim, I want to say "DateFormat" IS NOT THREAD SAFE. So always create it as a local variable or use it with thread local. – Aravind Yarram Dec 24 '10 at 22:24
  • @Pangea just figured out that the 31st of February doesn't throw an exception if we use the date format as "dd-mm-yyyy" it has to be "dd-MM-yyyy" that was the little thing worrying me from days, just noticed that i was using small letter for month, any way thanks. Any idea what the small m's mean ? – sasidhar Dec 25 '10 at 05:42
  • small m is used to indicate minutes. seems like SimpleDateFormat is more forgiving that desired ;-). Look here for the supported symbols and their interpretations: http://mboshart.dyndns.org/boshart/documentation/APIdocs/api/java/text/SimpleDateFormat.html#month – Aravind Yarram Dec 25 '10 at 05:50
2

looks like SimpleDateFormat is not checking the pattern strictly even after setLenient(false); method is applied on it, so i have used below method to validate if the date inputted is valid date or not as per supplied pattern.

import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public boolean isValidFormat(String dateString, String pattern) {
    boolean valid = true;
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
    try {
        formatter.parse(dateString);
    } catch (DateTimeParseException e) {
        valid = false;
    }
    return valid;
}
vijay
  • 609
  • 5
  • 9
1

Two comments on the use of SimpleDateFormat.

it should be declared as a static instance if declared as static access should be synchronized as it is not thread safe

IME that is better that instantiating an instance for each parse of a date.

Tom
  • 242
  • 2
  • 3
  • good point Tom. I updated the example I provided to make sure consistently using static instance. – tardate May 22 '09 at 04:04
  • Using a synchronized function might not scale for some projects. I would recommand putting the SimpleDateFormat in a ThreadLocal variable instead of a static variable accessed through a synchronized function. – user327961 May 08 '14 at 19:50
0

Above methods of date parsing are nice , i just added new check in existing methods that double check the converted date with original date using formater, so it works for almost each case as i verified. e.g. 02/29/2013 is invalid date. Given function parse the date according to current acceptable date formats. It returns true if date is not parsed successfully.

 public final boolean validateDateFormat(final String date) {
        String[] formatStrings = {"MM/dd/yyyy"};
        boolean isInvalidFormat = false;
        Date dateObj;
        for (String formatString : formatStrings) {
            try {
                SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
                sdf.applyPattern(formatString);
                sdf.setLenient(false);
                dateObj = sdf.parse(date);
                System.out.println(dateObj);
                if (date.equals(sdf.format(dateObj))) {
                    isInvalidFormat = false;
                    break;
                }
            } catch (ParseException e) {
                isInvalidFormat = true;
            }
        }
        return isInvalidFormat;
    }
Imran
  • 5,376
  • 2
  • 26
  • 45
0

Here's what I did for Node environment using no external libraries:

Date.prototype.yyyymmdd = function() {
   var yyyy = this.getFullYear().toString();
   var mm = (this.getMonth()+1).toString(); // getMonth() is zero-based
   var dd  = this.getDate().toString();
   return zeroPad([yyyy, mm, dd].join('-'));  
};

function zeroPad(date_string) {
   var dt = date_string.split('-');
   return dt[0] + '-' + (dt[1][1]?dt[1]:"0"+dt[1][0]) + '-' + (dt[2][1]?dt[2]:"0"+dt[2][0]);
}

function isDateCorrect(in_string) {
   if (!matchesDatePattern) return false;
   in_string = zeroPad(in_string);
   try {
      var idate = new Date(in_string);
      var out_string = idate.yyyymmdd();
      return in_string == out_string;
   } catch(err) {
      return false;
   }

   function matchesDatePattern(date_string) {
      var dateFormat = /[0-9]+-[0-9]+-[0-9]+/;
      return dateFormat.test(date_string); 
   }
}

And here is how to use it:

isDateCorrect('2014-02-23')
true
TennisVisuals
  • 427
  • 5
  • 8
0
// to return valid days of month, according to month and year
int returnDaysofMonth(int month, int year) {
    int daysInMonth;
    boolean leapYear;
    leapYear = checkLeap(year);
    if (month == 4 || month == 6 || month == 9 || month == 11)
        daysInMonth = 30;
    else if (month == 2)
        daysInMonth = (leapYear) ? 29 : 28;
    else
        daysInMonth = 31;
    return daysInMonth;
}

// to check a year is leap or not
private boolean checkLeap(int year) {
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, year);
    return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}
ashishdhiman2007
  • 807
  • 2
  • 13
  • 28
0

Here is I would check the date format:

 public static boolean checkFormat(String dateTimeString) {
    return dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}") || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}")
            || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}") || dateTimeString
            .matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z") ||
            dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}Z");
}
db07
  • 107
  • 1
  • 6
  • We have the modern [*java.time* classes](https://docs.oracle.com/javase/tutorial/datetime/TOC.html) for this work now. Writing complicated regex is not likely the best use of your time. See the one-liner in [my Answer](https://stackoverflow.com/a/39649815/642706). – Basil Bourque Oct 31 '18 at 22:43
0
        public static String detectDateFormat(String inputDate, String requiredFormat) {
        String tempDate = inputDate.replace("/", "").replace("-", "").replace(" ", "");
        String dateFormat;

        if (tempDate.matches("([0-12]{2})([0-31]{2})([0-9]{4})")) {
            dateFormat = "MMddyyyy";
        } else if (tempDate.matches("([0-31]{2})([0-12]{2})([0-9]{4})")) {
            dateFormat = "ddMMyyyy";
        } else if (tempDate.matches("([0-9]{4})([0-12]{2})([0-31]{2})")) {
            dateFormat = "yyyyMMdd";
        } else if (tempDate.matches("([0-9]{4})([0-31]{2})([0-12]{2})")) {
            dateFormat = "yyyyddMM";
        } else if (tempDate.matches("([0-31]{2})([a-z]{3})([0-9]{4})")) {
            dateFormat = "ddMMMyyyy";
        } else if (tempDate.matches("([a-z]{3})([0-31]{2})([0-9]{4})")) {
            dateFormat = "MMMddyyyy";
        } else if (tempDate.matches("([0-9]{4})([a-z]{3})([0-31]{2})")) {
            dateFormat = "yyyyMMMdd";
        } else if (tempDate.matches("([0-9]{4})([0-31]{2})([a-z]{3})")) {
            dateFormat = "yyyyddMMM";
        } else {
            return "Pattern Not Added";
//add your required regex
        }
        try {
            String formattedDate = new SimpleDateFormat(requiredFormat, Locale.ENGLISH).format(new SimpleDateFormat(dateFormat).parse(tempDate));

            return formattedDate;
        } catch (Exception e) {
            //
            return "";
        }

    }
0

setLenient to false if you like a strict validation

public boolean isThisDateValid(String dateToValidate, String dateFromat){

    if(dateToValidate == null){
        return false;
    }

    SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
    sdf.setLenient(false);

    try {

        //if not valid, it will throw ParseException
        Date date = sdf.parse(dateToValidate);
        System.out.println(date);

    } catch (ParseException e) {

        e.printStackTrace();
        return false;
    }

    return true;
}
Pravin Bansal
  • 4,315
  • 1
  • 28
  • 19
0

With 'legacy' date format, we can format the result and compare it back to the source.

    public boolean isValidFormat(String source, String pattern) {
    SimpleDateFormat sd = new SimpleDateFormat(pattern);
    sd.setLenient(false);
    try {
        Date date = sd.parse(source);
        return date != null && sd.format(date).equals(source);
    } catch (Exception e) {
        return false;
    }
}

This execerpt says 'false' to source=01.01.04 with pattern '01.01.2004'

skay
  • 1,681
  • 1
  • 14
  • 13
0

We can use the org.apache.commons.validator.GenericValidator's method directly without adding the whole library:

public static boolean isValidDate(String value, String datePattern, boolean strict) {

    if (value == null
            || datePattern == null
            || datePattern.length() <= 0) {

        return false;
    }

    SimpleDateFormat formatter = new SimpleDateFormat(datePattern, Locale.ENGLISH);
    formatter.setLenient(false);

    try {
        formatter.parse(value);
    } catch(ParseException e) {
        return false;
    }

    if (strict && (datePattern.length() != value.length())) {
        return false;
    }

    return true;
}
SANAT
  • 8,489
  • 55
  • 66
  • 1
    I recommend not. The `SimpleDateFormat` class used in that Apache code is notoriously troublesome and long outdated. Also the length check is not sound (though you can probably find corner cases where it gives the expected result). – Ole V.V. Jun 21 '21 at 17:16
  • `isValidDate("August 14, 2021", "MMMM d, yyyy", true)`, which I believe to be 100 % valid, yields `false`. And `isValidDate("May 9, 2021?", "MMMM d, yyyy", true)`, with the question mark that should not be allowed there, yields `true`. – Ole V.V. Jun 21 '21 at 17:20
0

A simple and elegant way for Android developers (Java 8 not required):

// month value is 1-based. e.g., 1 for January.
public static boolean isDateValid(int year, int month, int day) {
    Calendar calendar = Calendar.getInstance();
    try {
        calendar.setLenient(false);
        calendar.set(year, month-1, day);
        calendar.getTime();
        return true;
    } catch (Exception e) {
        return false;
    }
}
ucMedia
  • 4,105
  • 4
  • 38
  • 46
0

Below code works with dd/MM/yyyy format and can be used to check NotNull,NotEmpty as well.

public static boolean validateJavaDate(String strDate) {

    if (strDate != null && !strDate.isEmpty() && !strDate.equalsIgnoreCase(" ")) {
        {

            SimpleDateFormat date = new SimpleDateFormat("dd/MM/yyyy");
            date.setLenient(false);

            try {
                Date javaDate = date.parse(strDate);
                System.out.println(strDate + " Valid Date format");
            }

            catch (ParseException e) {
                System.out.println(strDate + " Invalid Date format");
                return false;
            }
            return true;
        }

    } else {
        System.out.println(strDate + "----> Date is Null/Empty");
        return false;
    }

}
  • 5
    Thank you for wanting to contribute. Please don’t teach the young ones to use the long outdated and notoriously troublesome `SimpleDateFormat` class. At least not as the first option. And not without any reservation. We have so much better in [`java.time`, the modern Java date and time API,](https://docs.oracle.com/javase/tutorial/datetime/) and its `DateTimeFormatter`. Also are you contributing anything new? The checking for null and empty is already in the answer by SANAT. – Ole V.V. Aug 21 '22 at 17:21
0

A simple way without using any external library would be:

private static final Pattern p_DATE = Pattern.compile("^\\s*(\\d+)-(\\d+)-(\\d+)\\s*$");

private static Date parseDate(String v) {
    
    if ( v==null || v.isBlank() ) {
        return null;
    }
    
    // 2023-03-02
    Matcher m = p_DATE.matcher(v);
    
    if ( ! m.find() ) {
        return null;
    }
    
    int day = Integer.parseInt(m.group(3), 10);
    int mon = Integer.parseInt(m.group(2), 10);
    int year = Integer.parseInt(m.group(1), 10);
    
    // convert years prior to 99
    if ( year<99 ) {
        year += 2000;
    }
    
    if ( 
            (day<0 || day>31) ||
            (mon<1 || mon>12) 
    ) {
        return null;
    }
    
    // validate max day
    int maxDayOfTheMonth = getMaxDayOfTheMonth(mon, year);

    if ( day > maxDayOfTheMonth ) {
        return null;
    }
    
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH, day);
    cal.set(Calendar.MONTH, mon-1);
    cal.set(Calendar.YEAR, year);
    
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    
    return cal.getTime();
}

private static int getMaxDayOfTheMonth(int mon, int year) {
    
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.DAY_OF_MONTH, 1);
    cal.set(Calendar.MONTH, mon-1);
    cal.set(Calendar.YEAR, year);
    
    cal.add(Calendar.MONTH, 1);
    cal.add(Calendar.DAY_OF_YEAR, -1);
    
    return cal.get(Calendar.DAY_OF_MONTH);
}
mgo1977
  • 81
  • 3
  • 1
    Strongly discouraged. The `Calendar` class was cumbersome to work with and for that reason was supplanted by java.time, the modern Java date and time API, nearly 10 years ago. Use java.time and your task will be an immense lot easier and simpler. It’s built-in since Java 8. Also no one should wnat an old-fashioned `Date` since that Java version. I agree not to use any external library. – Ole V.V. Mar 31 '23 at 19:57
  • Compare your 60 lines, some of which I don’t understand readily, with the 6 lines solution in [the good answer by Basil Bourque](https://stackoverflow.com/a/39649815/5772882). How can you call your way simple? I can’t make any sense of that claim. – Ole V.V. Mar 31 '23 at 20:03