在 Spring 中使用验证将 ZonedDateTime 字段限制为毫秒时间的 3 位数字

Using validation in Spring to restrict ZonedDateTime field to 3 digits at millisecond time

我在 Spring MVC 请求中有一个要验证的输入 DTO,特别是 ZonedDateTime 字段最多应包含 3 位毫秒级别的数字,即它不能是完整的纳秒精度。除此之外,输入请求应遵循 ISO 日期时间格式。我可以将该字段设为字符串,然后使用正则表达式对其进行限制,但我更愿意将其保留为 ZonedDateTime,这样我就不需要再次解析它了。

对象看起来像这样:

@Data
public class MeasurementDTO {

    private ZonedDateTime date;
    @Digits(integer = 20, fraction = 8) private BigDecimal value;

}

这是一个嵌套的 DTO,其中父对象以带有 @Valid 注释的 @RequestBody 形式出现。

我试过@JsonFormat,但我似乎无法限制毫秒部分。 有什么办法可以做到这一点,还是我应该自己将其解析为 String 然后处理它?或者甚至只是将它留在 ZonedDateTime,然后检查纳秒组件以查看它是否存在于自定义验证器中?

多亏了 Tim 的回答,我记得 Java Dates 的精度并不比毫秒高,所以我更新了问题以使用 ZonedDateTimes,它的精度高达纳秒精度。如果用户确实尝试传递更精确的信息,我确实希望能够向用户发出警告,如果使用 Date,此信息将被吞没。

您可能认为这不是一个完整的答案,但 java.util.Date 只存储最多毫秒的精度,并且不会超出此范围。请参见 Jon Skeet 的 answer here, or read the source code for CalendarDate,其中揭示了它除了毫秒存储之外什么都没有。

因此,使用 Hibernate 验证将 Date 限制为毫秒精度是没有意义的,因为类型本身已经带有此限制。

我用自定义验证器以困难的方式做到了这一点。不过,我仍然欢迎以更简洁的方式做到这一点的答案。

这是我的解决方案:

我添加了注释@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 {};
}

并实现了自定义验证器

public class MeasurementValidator implements ConstraintValidator<ValidMeasurementInput, MetricsDTO> {

    @Override
    public boolean isValid(MetricsDTO metricsDTO, ConstraintValidatorContext context) {
        ...
    }
}

此 class 中的某个地方以及其他一些验证是此代码:

   int nano = measurementDTO.getDate().getNano();
   int remainderAfterMillis = nano % 1000000;
   if (remainderAfterMillis != 0)
       valid = false;

当然,我将 @ValidMeasurementInput 添加到我的 DTO。