Reputation: 6331
I have an input DTO in a Spring MVC request that I want to validate, specifically the ZonedDateTime field should at most contain 3 digits at millisecond level, i.e. it cannot be the full nanoseconds precision. Other than that the input request should follow the ISO datetime format. I could make the field a String and then just restrict it with a regex but I prefer to keep it as an ZonedDateTime so I don't need to parse it again.
The object looks like this:
@Data
public class MeasurementDTO {
private ZonedDateTime date;
@Digits(integer = 20, fraction = 8) private BigDecimal value;
}
And this is a nested DTO where the parent object comes in as a @RequestBody
with the @Valid
annotation.
I've tried @JsonFormat but I can't seem to restrict the millisecond part. Is there any way to do this or should I just parse it myself as a String and then deal with it? Or even just leave it at ZonedDateTime and then check the nanosecond component to see if it's there in a custom validator?
Thanks to Tim's answer I remembered Java Date
s don't have more precision than millis anyway, so I updated the question to use ZonedDateTime
s which do have up to nanosecond precision. I do want to be able to give the user a warning if they do attempt to pass more precision, if using Date this information would just get swallowed.
Upvotes: 0
Views: 1919
Reputation: 6331
I did this the hard way with a custom validator. I am still welcoming answers that do this in a more concise way though.
Here's my solution:
I added an annotation @ValidMeasurementInput
@Documented
@Constraint(validatedBy = MeasurementValidator.class)
@Target({TYPE, FIELD, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface ValidMeasurementInput {
String message() default "Invalid measurement input";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And implemented a custom validator
public class MeasurementValidator implements ConstraintValidator<ValidMeasurementInput, MetricsDTO> {
@Override
public boolean isValid(MetricsDTO metricsDTO, ConstraintValidatorContext context) {
...
}
}
Somewhere in this class among some other validations is this code:
int nano = measurementDTO.getDate().getNano();
int remainderAfterMillis = nano % 1000000;
if (remainderAfterMillis != 0)
valid = false;
And of course I added the @ValidMeasurementInput
to my DTO.
Upvotes: 0
Reputation: 521437
You may not consider this to be a complete answer, but java.util.Date
only stores precision up to milliseconds, and not beyond that. See Jon Skeet's answer here, or read the source code for CalendarDate, which reveals that it has nothing beyond millisecond storage.
So, there is no sense in restricting a Date
to millisecond precision with Hibernate validation, because the type itself already carries this restriction.
Upvotes: 1