diff --git a/src/main/java/org/springframework/data/elasticsearch/core/convert/ElasticsearchDateConverter.java b/src/main/java/org/springframework/data/elasticsearch/core/convert/ElasticsearchDateConverter.java index a09c95ab7..6f2a0630d 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/convert/ElasticsearchDateConverter.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/convert/ElasticsearchDateConverter.java @@ -17,7 +17,9 @@ package org.springframework.data.elasticsearch.core.convert; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.time.Instant; import java.time.temporal.TemporalAccessor; +import java.util.Date; import java.util.concurrent.ConcurrentHashMap; import org.elasticsearch.common.time.DateFormatter; @@ -73,9 +75,25 @@ final public class ElasticsearchDateConverter { * @return the formatted object */ public String format(TemporalAccessor accessor) { + + Assert.notNull(accessor, "accessor must not be null"); + return dateFormatter.format(accessor); } + /** + * Formats the given {@link TemporalAccessor} int a String + * + * @param date must not be {@literal null} + * @return the formatted object + */ + public String format(Date date) { + + Assert.notNull(date, "accessor must not be null"); + + return dateFormatter.format(Instant.ofEpochMilli(date.getTime())); + } + /** * Parses a String into an object * @@ -96,4 +114,14 @@ final public class ElasticsearchDateConverter { throw new ConversionException("could not create object of class " + type.getName(), e); } } + + /** + * Parses a String into a Date. + * + * @param input the String to parse, must not be {@literal null}. + * @return the new created object + */ + public Date parse(String input) { + return new Date(Instant.from(dateFormatter.parse(input)).toEpochMilli()); + } } diff --git a/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentProperty.java b/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentProperty.java index 5239b9107..a69f6fa20 100644 --- a/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentProperty.java +++ b/src/main/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentProperty.java @@ -17,6 +17,7 @@ package org.springframework.data.elasticsearch.core.mapping; import java.time.temporal.TemporalAccessor; import java.util.Arrays; +import java.util.Date; import java.util.List; import org.springframework.data.elasticsearch.annotations.DateFormat; @@ -95,11 +96,14 @@ public class SimpleElasticsearchPersistentProperty extends /** * Initializes an {@link ElasticsearchPersistentPropertyConverter} if this property is annotated as a Field with type * {@link FieldType#Date}, has a {@link DateFormat} set and if the type of the property is one of the Java8 temporal - * classes. + * classes or java.util.Date. */ private void initDateConverter() { Field field = findAnnotation(Field.class); - if (field != null && field.type() == FieldType.Date && TemporalAccessor.class.isAssignableFrom(getType())) { + boolean isTemporalAccessor = TemporalAccessor.class.isAssignableFrom(getType()); + boolean isDate = Date.class.isAssignableFrom(getType()); + + if (field != null && field.type() == FieldType.Date && (isTemporalAccessor || isDate)) { DateFormat dateFormat = field.format(); ElasticsearchDateConverter converter = null; @@ -119,13 +123,21 @@ public class SimpleElasticsearchPersistentProperty extends propertyConverter = new ElasticsearchPersistentPropertyConverter() { @Override public String write(Object property) { - return dateConverter.format((TemporalAccessor) property); + if (isTemporalAccessor) { + return dateConverter.format((TemporalAccessor) property); + } else { // must be Date + return dateConverter.format((Date) property); + } } @SuppressWarnings("unchecked") @Override public Object read(String s) { - return dateConverter.parse(s, (Class) getType()); + if (isTemporalAccessor) { + return dateConverter.parse(s, (Class) getType()); + } else { // must be date + return dateConverter.parse(s); + } } }; } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/convert/ElasticsearchDateConverterTests.java b/src/test/java/org/springframework/data/elasticsearch/core/convert/ElasticsearchDateConverterTests.java index a05f734b5..62ccd35b8 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/convert/ElasticsearchDateConverterTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/convert/ElasticsearchDateConverterTests.java @@ -3,6 +3,8 @@ package org.springframework.data.elasticsearch.core.convert; import static org.assertj.core.api.Assertions.*; import java.time.LocalDate; +import java.util.Date; +import java.util.GregorianCalendar; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -14,7 +16,7 @@ import org.springframework.data.elasticsearch.annotations.DateFormat; */ class ElasticsearchDateConverterTests { - @ParameterizedTest + @ParameterizedTest // DATAES-716 @EnumSource(DateFormat.class) void shouldCreateConvertersForAllKnownFormats(DateFormat dateFormat) { @@ -28,8 +30,8 @@ class ElasticsearchDateConverterTests { assertThat(converter).isNotNull(); } - @Test - void shouldConvertToString() { + @Test // DATAES-716 + void shouldConvertTemporalAccessorToString() { LocalDate localDate = LocalDate.of(2019, 12, 27); ElasticsearchDateConverter converter = ElasticsearchDateConverter.of(DateFormat.basic_date); @@ -38,8 +40,8 @@ class ElasticsearchDateConverterTests { assertThat(formatted).isEqualTo("20191227"); } - @Test - void shouldParseFromString() { + @Test // DATAES-716 + void shouldParseTemporalAccessorFromString() { LocalDate localDate = LocalDate.of(2019, 12, 27); ElasticsearchDateConverter converter = ElasticsearchDateConverter.of(DateFormat.basic_date); @@ -47,4 +49,24 @@ class ElasticsearchDateConverterTests { assertThat(parsed).isEqualTo(localDate); } + + @Test // DATAES-792 + void shouldConvertLegacyDateToString() { + Date date = new GregorianCalendar(2020, 3, 19, 21, 44).getTime(); + ElasticsearchDateConverter converter = ElasticsearchDateConverter.of(DateFormat.basic_date_time); + + String formatted = converter.format(date); + + assertThat(formatted).isEqualTo("20200419T194400.000Z"); + } + + @Test // DATAES-792 + void shouldParseLegacyDateFromString() { + Date date = new GregorianCalendar(2020, 3, 19, 21, 44).getTime(); + ElasticsearchDateConverter converter = ElasticsearchDateConverter.of(DateFormat.basic_date_time); + + Date parsed = converter.parse("20200419T194400.000Z"); + + assertThat(parsed).isEqualTo(date); + } } diff --git a/src/test/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentPropertyUnitTests.java b/src/test/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentPropertyUnitTests.java index a64ddb0f6..79f45312b 100644 --- a/src/test/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentPropertyUnitTests.java +++ b/src/test/java/org/springframework/data/elasticsearch/core/mapping/SimpleElasticsearchPersistentPropertyUnitTests.java @@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.*; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; +import java.util.GregorianCalendar; import org.junit.jupiter.api.Test; import org.springframework.data.elasticsearch.annotations.DateFormat; @@ -69,25 +70,25 @@ public class SimpleElasticsearchPersistentPropertyUnitTests { assertThat(persistentProperty.getFieldName()).isEqualTo("by-value"); } - @Test - // DATAES-716 + @Test // DATAES-716, DATAES-792 void shouldSetPropertyConverters() { SimpleElasticsearchPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class); - ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("date"); - assertThat(persistentProperty.hasPropertyConverter()).isFalse(); - - persistentProperty = persistentEntity.getRequiredPersistentProperty("localDate"); + ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("localDate"); assertThat(persistentProperty.hasPropertyConverter()).isTrue(); assertThat(persistentProperty.getPropertyConverter()).isNotNull(); persistentProperty = persistentEntity.getRequiredPersistentProperty("localDateTime"); assertThat(persistentProperty.hasPropertyConverter()).isTrue(); assertThat(persistentProperty.getPropertyConverter()).isNotNull(); + + persistentProperty = persistentEntity.getRequiredPersistentProperty("legacyDate"); + assertThat(persistentProperty.hasPropertyConverter()).isTrue(); + assertThat(persistentProperty.getPropertyConverter()).isNotNull(); + } - @Test - // DATAES-716 + @Test // DATAES-716 void shouldConvertFromLocalDate() { SimpleElasticsearchPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class); ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("localDate"); @@ -98,8 +99,7 @@ public class SimpleElasticsearchPersistentPropertyUnitTests { assertThat(converted).isEqualTo("27.12.2019"); } - @Test - // DATAES-716 + @Test // DATAES-716 void shouldConvertToLocalDate() { SimpleElasticsearchPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class); ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("localDate"); @@ -110,6 +110,28 @@ public class SimpleElasticsearchPersistentPropertyUnitTests { assertThat(converted).isEqualTo(LocalDate.of(2019, 12, 27)); } + @Test // DATAES_792 + void shouldConvertFromLegacyDate() { + SimpleElasticsearchPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class); + ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("legacyDate"); + Date legacyDate = new GregorianCalendar(2020, 3, 19, 21, 44).getTime(); + + String converted = persistentProperty.getPropertyConverter().write(legacyDate); + + assertThat(converted).isEqualTo("20200419T194400.000Z"); + } + + @Test // DATES-792 + void shouldConvertToLegacyDate() { + SimpleElasticsearchPersistentEntity persistentEntity = context.getRequiredPersistentEntity(DatesProperty.class); + ElasticsearchPersistentProperty persistentProperty = persistentEntity.getRequiredPersistentProperty("legacyDate"); + + Object converted = persistentProperty.getPropertyConverter().read("20200419T194400.000Z"); + + assertThat(converted).isInstanceOf(Date.class); + assertThat(converted).isEqualTo(new GregorianCalendar(2020, 3, 19, 21, 44).getTime()); + } + static class InvalidScoreProperty { @Nullable @Score String scoreProperty; } @@ -123,8 +145,8 @@ public class SimpleElasticsearchPersistentPropertyUnitTests { } static class DatesProperty { - @Nullable @Field(type = FieldType.Date, format = DateFormat.basic_date) Date date; @Nullable @Field(type = FieldType.Date, format = DateFormat.custom, pattern = "dd.MM.uuuu") LocalDate localDate; @Nullable @Field(type = FieldType.Date, format = DateFormat.basic_date_time) LocalDateTime localDateTime; + @Nullable @Field(type = FieldType.Date, format = DateFormat.basic_date_time) Date legacyDate; } }