diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java index ea5085161..c9e746b78 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java @@ -15,6 +15,12 @@ */ package org.springframework.data.mongodb.util; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneOffset; +import java.time.temporal.Temporal; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -362,6 +368,25 @@ public class BsonUtils { return new BsonBinary(binary.getType(), binary.getData()); } + if(source instanceof Temporal) { + if (source instanceof Instant value) { + return new BsonDateTime(value.toEpochMilli()); + } + if (source instanceof LocalDateTime value) { + return new BsonDateTime(value.toInstant(ZoneOffset.UTC).toEpochMilli()); + } + if(source instanceof LocalDate value) { + return new BsonDateTime(value.atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli()); + } + if(source instanceof LocalTime value) { + return new BsonDateTime(value.atDate(LocalDate.ofEpochDay(0L)).toInstant(ZoneOffset.UTC).toEpochMilli()); + } + } + + if(source instanceof Date date) { + new BsonDateTime(date.getTime()); + } + throw new IllegalArgumentException(String.format("Unable to convert %s (%s) to BsonValue.", source, source != null ? source.getClass().getName() : "null")); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/AbstractEncryptionTestBase.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/AbstractEncryptionTestBase.java index 9360f2e78..606921b57 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/AbstractEncryptionTestBase.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/encryption/AbstractEncryptionTestBase.java @@ -21,6 +21,8 @@ import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; import static org.springframework.data.mongodb.core.query.Criteria.*; import java.security.SecureRandom; +import java.time.LocalDate; +import java.time.Month; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -89,6 +91,21 @@ public abstract class AbstractEncryptionTestBase { .loadedIsEqualToSource(); } + @Test // GH-4432 + void encryptAndDecryptJavaTime() { + + Person source = new Person(); + source.id = "id-1"; + source.today = LocalDate.of(1979, Month.SEPTEMBER, 18); + + template.save(source); + + verifyThat(source) // + .identifiedBy(Person::getId) // + .wasSavedMatching(it -> assertThat(it.get("today")).isInstanceOf(Binary.class)) // + .loadedIsEqualToSource(); + } + @Test // GH-4284 void encryptAndDecryptComplexValue() { @@ -548,6 +565,9 @@ public abstract class AbstractEncryptionTestBase { @ExplicitEncrypted(algorithm = AEAD_AES_256_CBC_HMAC_SHA_512_Random) // Map mapOfComplex; + @ExplicitEncrypted(algorithm = AEAD_AES_256_CBC_HMAC_SHA_512_Random) // + LocalDate today; + public String getId() { return this.id; } @@ -592,6 +612,10 @@ public abstract class AbstractEncryptionTestBase { return this.mapOfComplex; } + public LocalDate getToday() { + return today; + } + public void setId(String id) { this.id = id; } @@ -636,6 +660,10 @@ public abstract class AbstractEncryptionTestBase { this.mapOfComplex = mapOfComplex; } + public void setToday(LocalDate today) { + this.today = today; + } + @Override public boolean equals(Object o) { if (o == this) { @@ -650,13 +678,14 @@ public abstract class AbstractEncryptionTestBase { && Objects.equals(encryptedZip, person.encryptedZip) && Objects.equals(listOfString, person.listOfString) && Objects.equals(listOfComplex, person.listOfComplex) && Objects.equals(viaAltKeyNameField, person.viaAltKeyNameField) - && Objects.equals(mapOfString, person.mapOfString) && Objects.equals(mapOfComplex, person.mapOfComplex); + && Objects.equals(mapOfString, person.mapOfString) && Objects.equals(mapOfComplex, person.mapOfComplex) + && Objects.equals(today, person.today); } @Override public int hashCode() { return Objects.hash(id, name, ssn, wallet, address, encryptedZip, listOfString, listOfComplex, viaAltKeyNameField, - mapOfString, mapOfComplex); + mapOfString, mapOfComplex, today); } public String toString() { @@ -664,7 +693,8 @@ public abstract class AbstractEncryptionTestBase { + ", wallet=" + this.getWallet() + ", address=" + this.getAddress() + ", encryptedZip=" + this.getEncryptedZip() + ", listOfString=" + this.getListOfString() + ", listOfComplex=" + this.getListOfComplex() + ", viaAltKeyNameField=" + this.getViaAltKeyNameField() + ", mapOfString=" - + this.getMapOfString() + ", mapOfComplex=" + this.getMapOfComplex() + ")"; + + this.getMapOfString() + ", mapOfComplex=" + this.getMapOfComplex() + + ", today=" + this.getToday() + ")"; } }