1. Problem
-
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
. -
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 parameterT
for which the type is assignable -
It has a static method called
now
with a sole parameter whose type isjava.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.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.
3. Questions
-
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.
-
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?