Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Introduction

In this tutorial, we'll discuss the various ways to check if a String contains a valid date in Java.

We'll discuss the solutions before Java 8, after Java 8, and using the Apache Commons Validator.

2. Date Validation Overview

Whenever we receive data in any application, we need to verify that it's valid before doing any further processing.

In the case of date inputs, we may need to verify the following:

  • The input contains the date in a valid format, such as MM/DD/YYYY
  • The various parts of the input are in a valid range
  • The input resolves to a valid date in the calendar

We can use regular expressions to do the above. However, regular expressions to handle various input formats and locales are complex and error-prone. In addition, they can degrade performance.

We'll discuss the different ways to implement date validations in a flexible, robust, and efficient manner.

First, let's write an interface for the date validation:

public interface DateValidator {
   boolean isValid(String dateStr);
}

In the next sections, we'll implement this interface using the various approaches.

3. Validate Using DateFormat

Java has provided facilities to format and parse dates since the beginning. This functionality is in the DateFormat abstract class and its implementation — SimpleDateFormat.

Let's implement the date validation using the parse method of the DateFormat class:

public class DateValidatorUsingDateFormat implements DateValidator {
    private String dateFormat;

    public DateValidatorUsingDateFormat(String dateFormat) {
        this.dateFormat = dateFormat;
    }

    @Override
    public boolean isValid(String dateStr) {
        DateFormat sdf = new SimpleDateFormat(this.dateFormat);
        sdf.setLenient(false);
        try {
            sdf.parse(dateStr);
        } catch (ParseException e) {
            return false;
        }
        return true;
    }
}

Since the DateFormat and related classes are not thread-safe, we are creating a new instance for each method call.

Next, let's write the unit test for this class:

DateValidator validator = new DateValidatorUsingDateFormat("MM/dd/yyyy");

assertTrue(validator.isValid("02/28/2019"));        
assertFalse(validator.isValid("02/30/2019"));

This has been the most common solution before Java 8.

4. Validate Using LocalDate

Java 8 introduced an improved Date and Time API. It added the LocalDate class, which represents the date without time. This class is immutable and thread-safe.

LocalDate provides two static methods to parse dates. Both of them use a DateTimeFormatter to do the actual parsing:

public static LocalDate parse​(CharSequence text)
// parses dates using using DateTimeFormatter.ISO_LOCAL_DATE

public static LocalDate parse​(CharSequence text, DateTimeFormatter formatter)
// parses dates using the provided formatter

Let's use the parse method to implement the date validation:

public class DateValidatorUsingLocalDate implements DateValidator {
    private DateTimeFormatter dateFormatter;
    
    public DateValidatorUsingLocalDate(DateTimeFormatter dateFormatter) {
        this.dateFormatter = dateFormatter;
    }

    @Override
    public boolean isValid(String dateStr) {
        try {
            LocalDate.parse(dateStr, this.dateFormatter);
        } catch (DateTimeParseException e) {
            return false;
        }
        return true;
    }
}

The implementation uses a DateTimeFormatter object for formatting. Since this class is thread-safe, we're using the same instance across different method calls.

Let's also add a unit test for this implementation:

DateTimeFormatter dateFormatter = DateTimeFormatter.BASIC_ISO_DATE;
DateValidator validator = new DateValidatorUsingLocalDate(dateFormatter);
        
assertTrue(validator.isValid("20190228"));
assertFalse(validator.isValid("20190230"));

5. Validate Using DateTimeFormatter

In the previous section, we saw that LocalDate uses a DateTimeFormatter object for parsing. We can also use the DateTimeFormatter class directly for formatting and parsing.

DateTimeFormatter parses a text in two phases. In Phase 1, it parses the text into various date and time fields based on the configuration. In Phase 2, it resolves the parsed fields into a date and/or time object.

The ResolverStyle attribute controls phase 2. It is an enum having three possible values:

  • LENIENT – resolves dates and times leniently
  • SMART – resolves dates and times in an intelligent manner
  • STRICT – resolves dates and times strictly

Now, let's write the date validation using DateTimeFormatter directly:

public class DateValidatorUsingDateTimeFormatter implements DateValidator {
    private DateTimeFormatter dateFormatter;
    
    public DateValidatorUsingDateTimeFormatter(DateTimeFormatter dateFormatter) {
        this.dateFormatter = dateFormatter;
    }

    @Override
    public boolean isValid(String dateStr) {
        try {
            this.dateFormatter.parse(dateStr);
        } catch (DateTimeParseException e) {
            return false;
        }
        return true;
    }
}

Next, let's add the unit test for this class:

DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd", Locale.US)
    .withResolverStyle(ResolverStyle.STRICT);
DateValidator validator = new DateValidatorUsingDateTimeFormatter(dateFormatter);
        
assertTrue(validator.isValid("2019-02-28"));
assertFalse(validator.isValid("2019-02-30"));

In the above test, we're creating a DateTimeFormatter based on pattern and locale. We are using the strict resolution for dates.

6. Validate Using Apache Commons Validator

The Apache Commons project provides a validation framework. This contains validation routines, such as date, time, numbers, currency, IP address, email, and URL.

For our goal in this article, let's take a look at the GenericValidator class, which provides a couple of methods to check if a String contains a valid date:

public static boolean isDate(String value, Locale locale)
  
public static boolean isDate(String value,String datePattern, boolean strict)

To use the library, let's add the commons-validator Maven dependency to our project:

<dependency>
    <groupId>commons-validator</groupId>
    <artifactId>commons-validator</artifactId>
    <version>1.6</version>
</dependency>

Next, let's use the GenericValidator class to validate dates:

assertTrue(GenericValidator.isDate("2019-02-28", "yyyy-MM-dd", true));
assertFalse(GenericValidator.isDate("2019-02-29", "yyyy-MM-dd", true));

7. Conclusion

In this article, we looked at the various ways to check if a String contains a valid date.

As usual, the full source code can be found over on GitHub.

Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
Comments are closed on this article!