JSR 303 Validation for Dates

A common case is to validate date inputs in Java e.g. to avoid the nice and friendly typos like 06/01/1500.

Nevertheless by default where is not to much support to handle this very common case. So let’s fix this together:

Annotation first

The first we need is a JSR annotation which we use to annotate the code. We provide from the start proper default values, to avoid none sense data.

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

@Documented
// Note: We use here already a validator which we will add in a sec too
@Constraint(validatedBy = InDateRangeValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface InDateRange {
    // used to get later in the resource bundle the i18n text
    String message() default "{validation.date.InDateRange.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    // min value, we for now just a string
    String min() default "1900-01-01";
    // max date value we support
    String max() default "2999-12-31";
}

Validator next

As we have the annotation now, we still need to add some code behind for the JSR bean validation to make it really do something. I assume that both classes are in the same package.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class InDateRangeValidator implements ConstraintValidator<InDateRange, java.util.Date> {
    
    private final SimpleDateFormat dateParser = new SimpleDateFormat("yyyy-MM-dd");
    
    private InDateRange constraintAnnotation;
    
    @Override
    public void initialize(InDateRange constraintAnnotation) {
        this.constraintAnnotation = constraintAnnotation;
    }
    
    @Override
    public boolean isValid(java.util.Date value, ConstraintValidatorContext context) {
        try {
            final Date min = dateParser.parse(constraintAnnotation.min());
            final Date max = dateParser.parse(constraintAnnotation.max());
            return value == null ||
                    (value.after(min) && value.before(max));
        } catch (ParseException ex) {
            throw new RuntimeException(ex);
        }
    }
}

The validator is not very smart now, but it will do the job for. Let’s use it first, will we?

Note: This tutorial is a bit old, use nowadays the DateTimeFormatter from the Java time.

Apply our date validation

class SomeBean {
    @InDateRange
    private Date date;
}

Well not to surprising but the interesting part is of course always the test case we should add to verify all is good and working as we like it.

import java.util.Date;
import java.util.Set;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.BeforeClass;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.joda.time.LocalDate;

public class TestInDateRange {

    private static Validator validator;
    
    @BeforeClass
    public static void setUp() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
    }
    
    @Test
    public void testDateRange() {
        SomeBean test = new SomeBean();
        test.date = new LocalDate(10001, 01, 01).toDate();
        
        Set<ConstraintViolation<TestClass>> validate = validator.validate(test);
        System.out.println(validate);
        assertEquals(1, validate.size());
        assertEquals("date", validate.iterator().next().getPropertyPath().toString());
    }
}

i18n File

Assuming you use a default maven project we should add in /src/main/resources the file ValidationMessages.properties if it not already exists:

validation.date.InDateRange.message=date must be between {min} and {max}.
Note: You can use placeholders in the messages string. Here we use min and max, which automatically add the values from our annotation into the user message.

Going further

  • Well as you noticed right now we parse the min and max date each time. We could of course just do this once as it is quite static
  • We use currently java.util.Date well again something we could change
  • Last but not least we should try to avoid Date in the API / Model objects and try to just use simply UTC Longs

Paul Sterl has written 21 articles

2 thoughts on “JSR 303 Validation for Dates

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>