Jakarta Bean Validation

BVAL-496 Support for new date/time types (JSR 310)

1. Problem

  1. Java 8 comes with a whole new set of date/time related types, located in the java.time package. Those should be supported with @Past and @Future.

  2. The time and time zone to compare to during validation are currently fixed to the JVM’s (default) values. This should be made more flexible, so validation can be done for another time and/or time zone than the JVM’s values. Use cases: use a different time for testing, obtain the TZ from the currently logged in user, use the logical time of a batch job

2. Proposition

2.1. Mandate @Past/@Future support for TemporalAccessors

JSR-310 design intentionally encouraged end users to choose concrete types for every day programming, but ensured there were interfaces and classes that allowed third-party implementations to be treated as first-class citizens within the framework. Building on that premise and the different use cases that lead to the creation of the several java.time.temporal.TemporalAccessor implementations in Java SE itself (and some in related projects such as ThreeTen-Extra), basic support should be designed around the following requirements for the type:

  • It implements java.time.temporal.TemporalAccessor

  • It implements java.lang.Comparable using a parameter T for which the type is assignable

  • It has a static method called now with a sole parameter whose type is java.time.Clock

So, for any type TA that matches the above rules, @Past can be validated with code similar to:

//ClockProvider is documented below
Clock clock = clockProvider.getClock();
//TA is TemporalAccessor & Comparable
//Code for obtaining now static factory omitted
TA now = now.invoke(clock);
return t.compareTo(now) < 0;

This set of requirements makes the following Java SE types supported:

  • Specific points in the timeline: java.time.Instant, java.time.OffsetDateTime, java.time.ZonedDateTime

  • Wall-clock-like types: java.time.LocalDate, java.time.LocalDateTime, java.time.LocalTime

  • Other partial types: java.time.MonthDay, java.time.OffsetTime, java.time.Year, java.time.YearMonth

  • Non-ISO calendar types: java.time.chrono.HirajDate, java.time.chrono.JapaneseDate, java.time.chrono.MinguoDate, java.time.chrono.ThaiBuddhistDate

These rules would make the following Threeten-Extra types supported:

  • Partial types: org.threeten.extra.DayOfMonth, org.threeten.extra.DayOfYear, org.threeten.extra.YearWeek, org.threeten.extra.YearQuarter

2.1.1. Working around limitations in ConstraintValidator that prevent the above strategy

2.2. Make current instant customizable

To make the current instant customizable, a new SPI which returns java.time.Clock to be used by the validation engine for validating @Past and @Future constraints should be added:

    package javax.validation.spi;

    import java.time.Clock;

    public interface ClockProvider {
        Clock getClock();
    }

BV implementations must use a default ClockProvider which always returns java.time.Clock.systemDefault().

When bootstrapping a validator factory or validator, an alternative clock provider can be passed

  • via Configuration (also exposes the default clock provider)

  • via ValidatorContext

  • using XML

E.g.

Validator validator = Validation.byDefaultProvider()
    .configure()
    .clockProvider( myClockProvider() )
    .buildValidatorFactory()
    .getValidator();

Similar to message interpolators etc., custom clock providers are CDI-enabled if CDI is present, allowing to inject needed contextual information such as the current requests locale.

2.3. Extending @Past/@Future to support nowIsValid

In many valid use cases, these constraints should consider the current date/time, called now in JSR-310, as valid, especially when it comes to @Future. Therefore, both annotations should be changed to include a new attribute, nowIsValid (maybe nowAsValid), whose default is false in order to maintain backwards compatibility.

If the attribute is true, a return equals to 0 from compareTo will cause the constraint to be considered satisfied.

2.4. "Simple" TemporalAmount implementations support

2.5. Duration support

2.6. Period support

3. Questions

  1. Proposal 2. assumes that the current time + TZ can be obtained in a rather global fashion, i.e. the logical time of a currently running batch job or the TZ from the currently logged in user’s profile etc. Is there need to make this more contextual, i.e. expose the validated bean or similar? I can’t see a use case for this atm.

  2. One of the most common scenarios with date/time types (applies to ranges of all types though) is to have a start and end property for which start must be (sometimes equal or) greater than now and end must be (sometimes equal or) greater than start. Are we not supporting this somehow?