diff --git a/src/main/java/org/springframework/data/neo4j/core/convert/CypherTypes.java b/src/main/java/org/springframework/data/neo4j/core/convert/CypherTypes.java
index 0ca063c34..c42801dde 100644
--- a/src/main/java/org/springframework/data/neo4j/core/convert/CypherTypes.java
+++ b/src/main/java/org/springframework/data/neo4j/core/convert/CypherTypes.java
@@ -18,6 +18,7 @@ package org.springframework.data.neo4j.core.convert;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
+import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;
import java.util.ArrayList;
@@ -32,7 +33,7 @@ import org.springframework.data.convert.ConverterBuilder;
/**
* Conversions for all known Cypher types, directly supported by the driver. See
- * Working with Cypher values.
+ * Working with Cypher values.
*
* @author Michael J. Simons
* @since 6.0
@@ -56,6 +57,7 @@ final class CypherTypes {
hlp.add(ConverterBuilder.reading(Value.class, byte[].class, Value::asByteArray).andWriting(Values::value));
hlp.add(ConverterBuilder.reading(Value.class, LocalDate.class, Value::asLocalDate).andWriting(Values::value));
hlp.add(ConverterBuilder.reading(Value.class, OffsetTime.class, Value::asOffsetTime).andWriting(Values::value));
+ hlp.add(ConverterBuilder.reading(Value.class, OffsetDateTime.class, Value::asOffsetDateTime).andWriting(Values::value));
hlp.add(ConverterBuilder.reading(Value.class, LocalTime.class, Value::asLocalTime).andWriting(Values::value));
hlp.add(ConverterBuilder.reading(Value.class, ZonedDateTime.class, Value::asZonedDateTime).andWriting(Values::value));
hlp.add(ConverterBuilder.reading(Value.class, LocalDateTime.class, Value::asLocalDateTime).andWriting(Values::value));
diff --git a/src/main/java/org/springframework/data/neo4j/repository/query/PartValidator.java b/src/main/java/org/springframework/data/neo4j/repository/query/PartValidator.java
index 40eb515b4..b46d72090 100644
--- a/src/main/java/org/springframework/data/neo4j/repository/query/PartValidator.java
+++ b/src/main/java/org/springframework/data/neo4j/repository/query/PartValidator.java
@@ -18,6 +18,8 @@ package org.springframework.data.neo4j.repository.query;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;
import java.util.Arrays;
@@ -50,12 +52,12 @@ class PartValidator {
/**
* A set of the temporal types that are directly passable to the driver and support a meaningful comparison in a
* temporal sense (after, before). See
- *
+ *
*/
private static final Set> COMPARABLE_TEMPORAL_TYPES;
static {
Set> hlp = new TreeSet<>(Comparator.comparing(Class::getName));
- hlp.addAll(Arrays.asList(LocalDate.class, OffsetTime.class, ZonedDateTime.class, LocalDateTime.class, Instant.class));
+ hlp.addAll(Arrays.asList(LocalDate.class, OffsetTime.class, OffsetDateTime.class, LocalTime.class, ZonedDateTime.class, LocalDateTime.class, Instant.class));
COMPARABLE_TEMPORAL_TYPES = Collections.unmodifiableSet(hlp);
}
diff --git a/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java b/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java
index 1aeeabdea..65a8acf74 100644
--- a/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java
+++ b/src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java
@@ -24,6 +24,9 @@ import static org.assertj.core.api.Assertions.tuple;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.ArrayList;
@@ -36,6 +39,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.TimeZone;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -133,6 +137,7 @@ import org.springframework.data.neo4j.integration.shared.common.Inheritance;
import org.springframework.data.neo4j.integration.shared.common.KotlinPerson;
import org.springframework.data.neo4j.integration.shared.common.LikesHobbyRelationship;
import org.springframework.data.neo4j.integration.shared.common.MultipleLabels;
+import org.springframework.data.neo4j.integration.shared.common.OffsetTemporalEntity;
import org.springframework.data.neo4j.integration.shared.common.OneToOneSource;
import org.springframework.data.neo4j.integration.shared.common.OneToOneTarget;
import org.springframework.data.neo4j.integration.shared.common.ParentNode;
@@ -4372,6 +4377,26 @@ class RepositoryIT {
}
}
+ @Test // GH-2706
+ void findByOffsetDateTimeShouldWork(@Autowired TemporalRepository temporalRepository) {
+
+ temporalRepository.deleteAll();
+
+ LocalDateTime fixedDateTime = LocalDateTime.of(2023, 1, 1, 21, 21, 0);
+ ZoneId europeBerlin = TimeZone.getTimeZone("Europe/Berlin").toZoneId();
+ OffsetDateTime v1 = OffsetDateTime.of(fixedDateTime, europeBerlin.getRules().getOffset(fixedDateTime));
+ LocalTime v2 = fixedDateTime.toLocalTime();
+
+ temporalRepository.save(new OffsetTemporalEntity(v1, v2));
+ temporalRepository.save(new OffsetTemporalEntity(v1.minusDays(2), v2.minusMinutes(2)));
+
+ assertThat(temporalRepository.findAllByProperty1After(v1)).isEmpty();
+ assertThat(temporalRepository.findAllByProperty2After(v2)).isEmpty();
+
+ assertThat(temporalRepository.findAllByProperty1After(v1.minusDays(1))).hasSize(1);
+ assertThat(temporalRepository.findAllByProperty2After(v2.minusMinutes(1))).hasSize(1);
+ }
+
/**
* The tests in this class ensure that in case of an inheritance scenario no DTO is projected but the extending class
* is used. If it wasn't the case, we wouldn't find the relationship nor the other attribute.
@@ -4701,6 +4726,15 @@ class RepositoryIT {
interface EntityWithCustomIdAndDynamicLabelsRepository
extends Neo4jRepository {}
+ interface TemporalRepository extends
+ Neo4jRepository {
+
+ List findAllByProperty1After(OffsetDateTime aValue);
+
+ List findAllByProperty2After(LocalTime aValue);
+
+ }
+
@SpringJUnitConfig(Config.class)
static abstract class IntegrationTestBase {
diff --git a/src/test/java/org/springframework/data/neo4j/integration/shared/common/OffsetTemporalEntity.java b/src/test/java/org/springframework/data/neo4j/integration/shared/common/OffsetTemporalEntity.java
new file mode 100644
index 000000000..c9d0bada4
--- /dev/null
+++ b/src/test/java/org/springframework/data/neo4j/integration/shared/common/OffsetTemporalEntity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2011-2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.neo4j.integration.shared.common;
+
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.util.UUID;
+
+import org.springframework.data.neo4j.core.schema.GeneratedValue;
+import org.springframework.data.neo4j.core.schema.Id;
+import org.springframework.data.neo4j.core.schema.Node;
+
+/**
+ * Using some offset temporal types.
+ *
+ * @author Michael J. Simons
+ */
+@Node
+public class OffsetTemporalEntity {
+
+ @Id
+ @GeneratedValue
+ private UUID uuid;
+
+ private OffsetDateTime property1;
+
+ private LocalTime property2;
+
+ public OffsetTemporalEntity(OffsetDateTime property1, LocalTime property2) {
+ this.property1 = property1;
+ this.property2 = property2;
+ }
+
+ public UUID getUuid() {
+ return uuid;
+ }
+
+ public OffsetDateTime getProperty1() {
+ return property1;
+ }
+
+ public void setProperty1(OffsetDateTime property1) {
+ this.property1 = property1;
+ }
+
+ public LocalTime getProperty2() {
+ return property2;
+ }
+
+ public void setProperty2(LocalTime property2) {
+ this.property2 = property2;
+ }
+}
diff --git a/src/test/java/org/springframework/data/neo4j/repository/support/Neo4jRepositoryFactoryTest.java b/src/test/java/org/springframework/data/neo4j/repository/support/Neo4jRepositoryFactoryTest.java
index 28f62917c..8a02e3d80 100644
--- a/src/test/java/org/springframework/data/neo4j/repository/support/Neo4jRepositoryFactoryTest.java
+++ b/src/test/java/org/springframework/data/neo4j/repository/support/Neo4jRepositoryFactoryTest.java
@@ -122,7 +122,7 @@ class Neo4jRepositoryFactoryTest {
void validateTemporalShouldWork() {
assertThatExceptionOfType(QueryCreationException.class).isThrownBy(() -> repositoryFactory.getRepository(InvalidTemporal.class))
- .withMessageMatching("Could not create query for .*: The keywords \\[IsAfter, After] work only with properties with one of the following types: \\[class java.time.Instant, class java.time.LocalDate, class java.time.LocalDateTime, class java.time.OffsetTime, class java.time.ZonedDateTime]");
+ .withMessageMatching("Could not create query for .*: The keywords \\[IsAfter, After] work only with properties with one of the following types: \\[class java.time.Instant, class java.time.LocalDate, class java.time.LocalDateTime, class java.time.LocalTime, class java.time.OffsetDateTime, class java.time.OffsetTime, class java.time.ZonedDateTime]");
}
@Test