Регистрация нового преобразователя даты Auditable в Spring Data MongoDB для ZonedDateTime

Я хочу, чтобы мой документ (@CreatedDate и @LastModifiedDate) MongoDB работал с полями ZonedDateTime.

По-видимому, этот тип не поддерживается Spring Data (org.springframework.data.auditing.AnnotationAuditingMetadata. org.springframework.data.auditing.AnnotationAuditingMetadata).

Рамочная версия: Spring Boot 2.0.0 и Spring Data MongoDB 2.0.0

Ошибка проверки данных Spring Data:

java.lang.IllegalArgumentException: Invalid date type for member <MEMBER NAME>!
Supported types are [org.joda.time.DateTime, org.joda.time.LocalDateTime, java.util.Date, java.lang.Long, long].

Монгольская конфигурация:

@Configuration
@EnableMongoAuditing
public class MongoConfiguration {

}

Аудиторская организация:

public abstract class BaseDocument {

    @CreatedDate
    private ZonedDateTime createdDate;

    @LastModifiedDate
    private ZonedDateTime lastModifiedDate;

}

Вещи, которые я пробовал

Я также попытался создать собственный конвертер для ZonedDateTime, но он не учитывается Spring Data. Класс DateConvertingAuditableBeanWrapper имеет ConversionService который настроен в методе конструктора с помощью JodaTimeConverters, Jsr310Converters и ThreeTenBackPortConverters.

Пользовательский конвертер:

@Component
public class LocalDateTimeToZonedDateTimeConverter implements Converter<LocalDateTime, ZonedDateTime> {

    @Override
    public ZonedDateTime convert(LocalDateTime source) {
        return source.atZone(ZoneId.systemDefault());
    }

}

Spring Data DateConvertingAuditableBeanWrapper:

class DefaultAuditableBeanWrapperFactory implements AuditableBeanWrapperFactory {

    abstract static class DateConvertingAuditableBeanWrapper implements AuditableBeanWrapper {

        private final ConversionService conversionService;

    }
}

Можно ли ZonedDateTime поля ZonedDateTime?

Как я могу зарегистрировать конвертер?

Ответ 1

Создайте DateTimeProvider чтобы указать текущее время, которое будет использоваться при аудите:

@Component("dateTimeProvider")
public class CustomDateTimeProvider implements DateTimeProvider {

    @Override
    public Optional<TemporalAccessor> getNow() {
        return Optional.of(ZonedDateTime.now());
    }
}

А потом:

@Configuration
@EnableMongoAuditing(dateTimeProviderRef = "dateTimeProvider")
public class MongoConfiguration {

    @Bean
    public MongoCustomConversions customConversions() {
        List<Converter<?, ?>> converters = new ArrayList<>();
        converters.add(new DateToZonedDateTimeConverter());
        converters.add(new ZonedDateTimeToDateConverter());
        return new MongoCustomConversions(converters);
    }

    class DateToZonedDateTimeConverter implements Converter<Date, ZonedDateTime> {

        @Override
        public ZonedDateTime convert(Date source) {
            return source == null ? null : 
                    ZonedDateTime.ofInstant(source.toInstant(), ZoneId.systemDefault());
        }
    }

    class ZonedDateTimeToDateConverter implements Converter<ZonedDateTime, Date> {

        @Override
        public Date convert(ZonedDateTime source) {
            return source == null ? null : Date.from(source.toInstant());
        }
    }
}

Однако я бы не стал использовать ZonedDateTime для этой цели. Желательно придерживаться OffsetDateTime:

OffsetDateTime, ZonedDateTime и Instant все хранят момент времени на временной ZonedDateTime с точностью до наносекунды. Мгновенное является самым простым, просто представляющим момент. OffsetDateTime добавляет к OffsetDateTime смещение от UTC/Гринвич, что позволяет получить местную дату-время. ZonedDateTime добавляет полные правила часовых поясов.

Предполагается, что ZonedDateTime или Instant используется для моделирования данных в более простых приложениях. Этот класс может использоваться при более подробном моделировании концепций даты и времени или при обмене данными с базой данных или в сетевом протоколе.