From ddd89bb0bf7b0d4dbe331e5905ddc7dc929c1226 Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Thu, 21 Sep 2023 16:17:11 +0300 Subject: [PATCH 1/7] Instant support --- .../firestore/util/CustomClassMapper.java | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java index 9852524f51b..916db21687e 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java @@ -18,6 +18,8 @@ import static com.google.firebase.firestore.util.ApiUtil.newInstance; import android.net.Uri; +import android.os.Build; +import androidx.annotation.RequiresApi; import com.google.firebase.Timestamp; import com.google.firebase.firestore.Blob; import com.google.firebase.firestore.DocumentId; @@ -41,6 +43,7 @@ import java.lang.reflect.WildcardType; import java.net.URI; import java.net.URL; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -175,6 +178,9 @@ private static Object serialize(T o, ErrorPath path) { || o instanceof DocumentReference || o instanceof FieldValue) { return o; + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && o instanceof Instant) { + Instant instant = (Instant) o; + return new Timestamp(instant.getEpochSecond(), instant.getNano()); } else if (o instanceof Uri || o instanceof URI || o instanceof URL) { return o.toString(); } else { @@ -235,6 +241,8 @@ private static T deserializeToClass(Object o, Class clazz, DeserializeCon return (T) convertDate(o, context); } else if (Timestamp.class.isAssignableFrom(clazz)) { return (T) convertTimestamp(o, context); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && Instant.class.isAssignableFrom(clazz)) { + return (T) convertInstant(o, context); } else if (Blob.class.isAssignableFrom(clazz)) { return (T) convertBlob(o, context); } else if (GeoPoint.class.isAssignableFrom(clazz)) { @@ -508,6 +516,20 @@ private static Timestamp convertTimestamp(Object o, DeserializeContext context) } } + @RequiresApi(api = Build.VERSION_CODES.O) + private static Instant convertInstant(Object o, DeserializeContext context) { + if (o instanceof Timestamp) { + Timestamp timestamp = (Timestamp) o; + return Instant.ofEpochSecond(timestamp.getSeconds(), timestamp.getNanoseconds()); + } else if (o instanceof Date) { + return Instant.ofEpochMilli(((Date) o).getTime()); + } else { + throw deserializeError( + context.errorPath, + "Failed to convert value of type " + o.getClass().getName() + " to Instant"); + } + } + private static Blob convertBlob(Object o, DeserializeContext context) { if (o instanceof Blob) { return (Blob) o; @@ -919,13 +941,14 @@ Map serialize(T object, ErrorPath path) { private void applyFieldAnnotations(Field field) { if (field.isAnnotationPresent(ServerTimestamp.class)) { Class fieldType = field.getType(); - if (fieldType != Date.class && fieldType != Timestamp.class) { + if (fieldType != Date.class && fieldType != Timestamp.class + && ! (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && fieldType == Instant.class)) { throw new IllegalArgumentException( "Field " + field.getName() + " is annotated with @ServerTimestamp but is " + fieldType - + " instead of Date or Timestamp."); + + " instead of Date, Timestamp, or Instant."); } serverTimestamps.add(propertyName(field)); } @@ -940,13 +963,14 @@ private void applyFieldAnnotations(Field field) { private void applyGetterAnnotations(Method method) { if (method.isAnnotationPresent(ServerTimestamp.class)) { Class returnType = method.getReturnType(); - if (returnType != Date.class && returnType != Timestamp.class) { + if (returnType != Date.class && returnType != Timestamp.class + && ! (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && returnType == Instant.class)) { throw new IllegalArgumentException( "Method " + method.getName() + " is annotated with @ServerTimestamp but returns " + returnType - + " instead of Date or Timestamp."); + + " instead of Date, Timestamp, or Instant."); } serverTimestamps.add(propertyName(method)); } From 8fb289db9a2b71b780909752d756053d5b8f0d93 Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Wed, 16 Apr 2025 22:49:54 +0300 Subject: [PATCH 2/7] fix formatting --- .../firestore/util/CustomClassMapper.java | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java index ff32a983a20..c1399013e97 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java @@ -14,24 +14,6 @@ package com.google.firebase.firestore.util; -import static com.google.firebase.firestore.util.ApiUtil.invoke; -import static com.google.firebase.firestore.util.ApiUtil.newInstance; - -import android.net.Uri; -import android.os.Build; -import androidx.annotation.RequiresApi; -import com.google.firebase.Timestamp; -import com.google.firebase.firestore.Blob; -import com.google.firebase.firestore.DocumentId; -import com.google.firebase.firestore.DocumentReference; -import com.google.firebase.firestore.Exclude; -import com.google.firebase.firestore.FieldValue; -import com.google.firebase.firestore.GeoPoint; -import com.google.firebase.firestore.IgnoreExtraProperties; -import com.google.firebase.firestore.PropertyName; -import com.google.firebase.firestore.ServerTimestamp; -import com.google.firebase.firestore.ThrowOnExtraProperties; -import com.google.firebase.firestore.VectorValue; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -57,6 +39,25 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import android.net.Uri; +import android.os.Build; +import androidx.annotation.RequiresApi; +import com.google.firebase.Timestamp; +import com.google.firebase.firestore.Blob; +import com.google.firebase.firestore.DocumentId; +import com.google.firebase.firestore.DocumentReference; +import com.google.firebase.firestore.Exclude; +import com.google.firebase.firestore.FieldValue; +import com.google.firebase.firestore.GeoPoint; +import com.google.firebase.firestore.IgnoreExtraProperties; +import com.google.firebase.firestore.PropertyName; +import com.google.firebase.firestore.ServerTimestamp; +import com.google.firebase.firestore.ThrowOnExtraProperties; +import com.google.firebase.firestore.VectorValue; + +import static com.google.firebase.firestore.util.ApiUtil.invoke; +import static com.google.firebase.firestore.util.ApiUtil.newInstance; + /** Helper class to convert to/from custom POJO classes and plain Java types. */ public class CustomClassMapper { /** Maximum depth before we give up and assume it's a recursive object graph. */ @@ -243,7 +244,8 @@ private static T deserializeToClass(Object o, Class clazz, DeserializeCon return (T) convertDate(o, context); } else if (Timestamp.class.isAssignableFrom(clazz)) { return (T) convertTimestamp(o, context); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && Instant.class.isAssignableFrom(clazz)) { + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && Instant.class.isAssignableFrom(clazz)) { return (T) convertInstant(o, context); } else if (Blob.class.isAssignableFrom(clazz)) { return (T) convertBlob(o, context); @@ -955,8 +957,9 @@ Map serialize(T object, ErrorPath path) { private void applyFieldAnnotations(Field field) { if (field.isAnnotationPresent(ServerTimestamp.class)) { Class fieldType = field.getType(); - if (fieldType != Date.class && fieldType != Timestamp.class - && ! (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && fieldType == Instant.class)) { + if (fieldType != Date.class + && fieldType != Timestamp.class + && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && fieldType == Instant.class)) { throw new IllegalArgumentException( "Field " + field.getName() @@ -977,8 +980,9 @@ private void applyFieldAnnotations(Field field) { private void applyGetterAnnotations(Method method) { if (method.isAnnotationPresent(ServerTimestamp.class)) { Class returnType = method.getReturnType(); - if (returnType != Date.class && returnType != Timestamp.class - && ! (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && returnType == Instant.class)) { + if (returnType != Date.class + && returnType != Timestamp.class + && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && returnType == Instant.class)) { throw new IllegalArgumentException( "Method " + method.getName() From 1fde57d7aa7d112b482c43f401741999e194dd55 Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Fri, 18 Apr 2025 02:11:33 +0300 Subject: [PATCH 3/7] fix formatting --- .../firestore/util/CustomClassMapper.java | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java index c1399013e97..63d82821d82 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java @@ -14,6 +14,24 @@ package com.google.firebase.firestore.util; +import static com.google.firebase.firestore.util.ApiUtil.invoke; +import static com.google.firebase.firestore.util.ApiUtil.newInstance; + +import android.net.Uri; +import android.os.Build; +import androidx.annotation.RequiresApi; +import com.google.firebase.Timestamp; +import com.google.firebase.firestore.Blob; +import com.google.firebase.firestore.DocumentId; +import com.google.firebase.firestore.DocumentReference; +import com.google.firebase.firestore.Exclude; +import com.google.firebase.firestore.FieldValue; +import com.google.firebase.firestore.GeoPoint; +import com.google.firebase.firestore.IgnoreExtraProperties; +import com.google.firebase.firestore.PropertyName; +import com.google.firebase.firestore.ServerTimestamp; +import com.google.firebase.firestore.ThrowOnExtraProperties; +import com.google.firebase.firestore.VectorValue; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -39,25 +57,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import android.net.Uri; -import android.os.Build; -import androidx.annotation.RequiresApi; -import com.google.firebase.Timestamp; -import com.google.firebase.firestore.Blob; -import com.google.firebase.firestore.DocumentId; -import com.google.firebase.firestore.DocumentReference; -import com.google.firebase.firestore.Exclude; -import com.google.firebase.firestore.FieldValue; -import com.google.firebase.firestore.GeoPoint; -import com.google.firebase.firestore.IgnoreExtraProperties; -import com.google.firebase.firestore.PropertyName; -import com.google.firebase.firestore.ServerTimestamp; -import com.google.firebase.firestore.ThrowOnExtraProperties; -import com.google.firebase.firestore.VectorValue; - -import static com.google.firebase.firestore.util.ApiUtil.invoke; -import static com.google.firebase.firestore.util.ApiUtil.newInstance; - /** Helper class to convert to/from custom POJO classes and plain Java types. */ public class CustomClassMapper { /** Maximum depth before we give up and assume it's a recursive object graph. */ From bad000d2356336ef80736b5e22d64724f5fdf44b Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Thu, 24 Apr 2025 19:47:26 +0300 Subject: [PATCH 4/7] Add support for `kotlinx.datetime.Instant`. Add tests. --- firebase-firestore/firebase-firestore.gradle | 1 + .../firestore/util/CustomClassMapper.java | 13 ++- .../firebase/firestore/util/MapperTest.java | 83 +++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/firebase-firestore/firebase-firestore.gradle b/firebase-firestore/firebase-firestore.gradle index 806babf6236..2a4a57f84a3 100644 --- a/firebase-firestore/firebase-firestore.gradle +++ b/firebase-firestore/firebase-firestore.gradle @@ -142,6 +142,7 @@ dependencies { implementation libs.grpc.stub implementation libs.kotlin.stdlib implementation libs.kotlinx.coroutines.core + implementation libs.kotlinx.datetime compileOnly libs.autovalue.annotations compileOnly libs.javax.annotation.jsr250 diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java index 63d82821d82..87925c481be 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java @@ -183,6 +183,10 @@ private static Object serialize(T o, ErrorPath path) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && o instanceof Instant) { Instant instant = (Instant) o; return new Timestamp(instant.getEpochSecond(), instant.getNano()); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && o instanceof kotlinx.datetime.Instant) { + kotlinx.datetime.Instant instant = (kotlinx.datetime.Instant) o; + return new Timestamp(instant.getEpochSeconds(), instant.getNanosecondsOfSecond()); } else if (o instanceof Uri || o instanceof URI || o instanceof URL) { return o.toString(); } else { @@ -246,6 +250,9 @@ private static T deserializeToClass(Object o, Class clazz, DeserializeCon } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && Instant.class.isAssignableFrom(clazz)) { return (T) convertInstant(o, context); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && kotlinx.datetime.Instant.class.isAssignableFrom(clazz)) { + return (T) new kotlinx.datetime.Instant(convertInstant(o, context)); } else if (Blob.class.isAssignableFrom(clazz)) { return (T) convertBlob(o, context); } else if (GeoPoint.class.isAssignableFrom(clazz)) { @@ -958,7 +965,8 @@ private void applyFieldAnnotations(Field field) { Class fieldType = field.getType(); if (fieldType != Date.class && fieldType != Timestamp.class - && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && fieldType == Instant.class)) { + && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && (fieldType == Instant.class || fieldType == kotlinx.datetime.Instant.class))) { throw new IllegalArgumentException( "Field " + field.getName() @@ -981,7 +989,8 @@ private void applyGetterAnnotations(Method method) { Class returnType = method.getReturnType(); if (returnType != Date.class && returnType != Timestamp.class - && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && returnType == Instant.class)) { + && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + && (returnType == Instant.class || returnType == kotlinx.datetime.Instant.class))) { throw new IllegalArgumentException( "Method " + method.getName() diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/util/MapperTest.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/util/MapperTest.java index bab4691f6f5..099a208d531 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/util/MapperTest.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/util/MapperTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.fail; import androidx.annotation.Nullable; +import com.google.firebase.Timestamp; import com.google.firebase.firestore.DocumentId; import com.google.firebase.firestore.DocumentReference; import com.google.firebase.firestore.Exclude; @@ -29,6 +30,7 @@ import com.google.firebase.firestore.TestUtil; import com.google.firebase.firestore.ThrowOnExtraProperties; import java.io.Serializable; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -37,6 +39,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import org.junit.Test; import org.robolectric.annotation.Config; @@ -95,6 +98,44 @@ public boolean isValue() { } } + private static class TimeBean { + public Timestamp timestamp; + public Date date; + public Instant instant; + public kotlinx.datetime.Instant instantKt; + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + TimeBean timeBean = (TimeBean) o; + return Objects.equals(timestamp, timeBean.timestamp) + && Objects.equals(date, timeBean.date) + && Objects.equals(instant, timeBean.instant) + && Objects.equals(instantKt, timeBean.instantKt); + } + + @Override + public int hashCode() { + return Objects.hash(timestamp, date, instant, instantKt); + } + + @Override + public String toString() { + return "TimeBean{" + + "_date=" + + date + + ", _timestamp=" + + timestamp + + ", _instant=" + + instant + + ", _instantKt=" + + instantKt + + '}'; + } + } + private static class ShortBean { private short value; @@ -1476,6 +1517,48 @@ public void serializeBooleanBean() { assertJson("{'value': true}", serialize(bean)); } + @Test + public void serializeTimeBean() { + TimeBean bean = new TimeBean(); + bean.instant = Instant.ofEpochSecond(1234, 5678); + bean.timestamp = new Timestamp(bean.instant); + bean.date = new Date(1234); + bean.instantKt = new kotlinx.datetime.Instant(bean.instant); + assertEquals( + Map.of( + "timestamp", + bean.timestamp, + "date", + bean.date, + "instant", + bean.timestamp, + "instantKt", + bean.timestamp), + serialize(bean)); + } + + @Test + public void deserializeTimeBean() { + TimeBean bean = new TimeBean(); + bean.instant = Instant.ofEpochSecond(1234, 5678); + bean.timestamp = new Timestamp(bean.instant); + bean.date = new Date(1234); + bean.instantKt = new kotlinx.datetime.Instant(bean.instant); + assertEquals( + bean, + convertToCustomClass( + Map.of( + "timestamp", + bean.timestamp, + "date", + bean.date, + "instant", + bean.timestamp, + "instantKt", + bean.timestamp), + TimeBean.class)); + } + @Test public void serializeFloatBean() { FloatBean bean = new FloatBean(); From a27d72dadd78f80b2ecc4610e4cacc1439cb9c48 Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Fri, 25 Apr 2025 01:01:48 +0300 Subject: [PATCH 5/7] Make `libs.kotlinx.datetime` `compileOnly`. --- firebase-firestore/firebase-firestore.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firebase-firestore/firebase-firestore.gradle b/firebase-firestore/firebase-firestore.gradle index 2a4a57f84a3..fc20741da79 100644 --- a/firebase-firestore/firebase-firestore.gradle +++ b/firebase-firestore/firebase-firestore.gradle @@ -142,10 +142,10 @@ dependencies { implementation libs.grpc.stub implementation libs.kotlin.stdlib implementation libs.kotlinx.coroutines.core - implementation libs.kotlinx.datetime compileOnly libs.autovalue.annotations compileOnly libs.javax.annotation.jsr250 + compileOnly libs.kotlinx.datetime annotationProcessor libs.autovalue @@ -163,6 +163,7 @@ dependencies { testImplementation libs.hamcrest.junit testImplementation libs.mockito.core testImplementation libs.robolectric + testImplementation libs.kotlinx.datetime testCompileOnly libs.protobuf.java From e967dd10359490f85782cf2c412234fcba88596e Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Thu, 2 Oct 2025 00:06:26 +0300 Subject: [PATCH 6/7] Remove `kotlinx.datetime.Instant` support --- firebase-firestore/firebase-firestore.gradle | 2 -- .../firestore/util/CustomClassMapper.java | 13 ++------ .../firebase/firestore/util/MapperTest.java | 30 +++---------------- 3 files changed, 6 insertions(+), 39 deletions(-) diff --git a/firebase-firestore/firebase-firestore.gradle b/firebase-firestore/firebase-firestore.gradle index ecdff9fd8a3..12da631f2ec 100644 --- a/firebase-firestore/firebase-firestore.gradle +++ b/firebase-firestore/firebase-firestore.gradle @@ -147,7 +147,6 @@ dependencies { compileOnly libs.autovalue.annotations compileOnly libs.javax.annotation.jsr250 - compileOnly libs.kotlinx.datetime annotationProcessor libs.autovalue @@ -165,7 +164,6 @@ dependencies { testImplementation libs.hamcrest.junit testImplementation libs.mockito.core testImplementation libs.robolectric - testImplementation libs.kotlinx.datetime testCompileOnly libs.protobuf.java diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java index 87925c481be..63d82821d82 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/util/CustomClassMapper.java @@ -183,10 +183,6 @@ private static Object serialize(T o, ErrorPath path) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && o instanceof Instant) { Instant instant = (Instant) o; return new Timestamp(instant.getEpochSecond(), instant.getNano()); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - && o instanceof kotlinx.datetime.Instant) { - kotlinx.datetime.Instant instant = (kotlinx.datetime.Instant) o; - return new Timestamp(instant.getEpochSeconds(), instant.getNanosecondsOfSecond()); } else if (o instanceof Uri || o instanceof URI || o instanceof URL) { return o.toString(); } else { @@ -250,9 +246,6 @@ private static T deserializeToClass(Object o, Class clazz, DeserializeCon } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && Instant.class.isAssignableFrom(clazz)) { return (T) convertInstant(o, context); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - && kotlinx.datetime.Instant.class.isAssignableFrom(clazz)) { - return (T) new kotlinx.datetime.Instant(convertInstant(o, context)); } else if (Blob.class.isAssignableFrom(clazz)) { return (T) convertBlob(o, context); } else if (GeoPoint.class.isAssignableFrom(clazz)) { @@ -965,8 +958,7 @@ private void applyFieldAnnotations(Field field) { Class fieldType = field.getType(); if (fieldType != Date.class && fieldType != Timestamp.class - && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - && (fieldType == Instant.class || fieldType == kotlinx.datetime.Instant.class))) { + && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && fieldType == Instant.class)) { throw new IllegalArgumentException( "Field " + field.getName() @@ -989,8 +981,7 @@ private void applyGetterAnnotations(Method method) { Class returnType = method.getReturnType(); if (returnType != Date.class && returnType != Timestamp.class - && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - && (returnType == Instant.class || returnType == kotlinx.datetime.Instant.class))) { + && !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && returnType == Instant.class)) { throw new IllegalArgumentException( "Method " + method.getName() diff --git a/firebase-firestore/src/test/java/com/google/firebase/firestore/util/MapperTest.java b/firebase-firestore/src/test/java/com/google/firebase/firestore/util/MapperTest.java index 099a208d531..e9b478de7c1 100644 --- a/firebase-firestore/src/test/java/com/google/firebase/firestore/util/MapperTest.java +++ b/firebase-firestore/src/test/java/com/google/firebase/firestore/util/MapperTest.java @@ -102,7 +102,6 @@ private static class TimeBean { public Timestamp timestamp; public Date date; public Instant instant; - public kotlinx.datetime.Instant instantKt; @Override public boolean equals(Object o) { @@ -112,13 +111,12 @@ public boolean equals(Object o) { TimeBean timeBean = (TimeBean) o; return Objects.equals(timestamp, timeBean.timestamp) && Objects.equals(date, timeBean.date) - && Objects.equals(instant, timeBean.instant) - && Objects.equals(instantKt, timeBean.instantKt); + && Objects.equals(instant, timeBean.instant); } @Override public int hashCode() { - return Objects.hash(timestamp, date, instant, instantKt); + return Objects.hash(timestamp, date, instant); } @Override @@ -130,8 +128,6 @@ public String toString() { + timestamp + ", _instant=" + instant - + ", _instantKt=" - + instantKt + '}'; } } @@ -1523,17 +1519,8 @@ public void serializeTimeBean() { bean.instant = Instant.ofEpochSecond(1234, 5678); bean.timestamp = new Timestamp(bean.instant); bean.date = new Date(1234); - bean.instantKt = new kotlinx.datetime.Instant(bean.instant); assertEquals( - Map.of( - "timestamp", - bean.timestamp, - "date", - bean.date, - "instant", - bean.timestamp, - "instantKt", - bean.timestamp), + Map.of("timestamp", bean.timestamp, "date", bean.date, "instant", bean.timestamp), serialize(bean)); } @@ -1543,19 +1530,10 @@ public void deserializeTimeBean() { bean.instant = Instant.ofEpochSecond(1234, 5678); bean.timestamp = new Timestamp(bean.instant); bean.date = new Date(1234); - bean.instantKt = new kotlinx.datetime.Instant(bean.instant); assertEquals( bean, convertToCustomClass( - Map.of( - "timestamp", - bean.timestamp, - "date", - bean.date, - "instant", - bean.timestamp, - "instantKt", - bean.timestamp), + Map.of("timestamp", bean.timestamp, "date", bean.date, "instant", bean.timestamp), TimeBean.class)); } From 9b33b3ef28c336973982c6f2d435f75df70b9923 Mon Sep 17 00:00:00 2001 From: eranl <1707552+eranl@users.noreply.github.com> Date: Thu, 2 Oct 2025 01:34:36 +0300 Subject: [PATCH 7/7] Add `CHANGELOG` entry. --- firebase-firestore/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/firebase-firestore/CHANGELOG.md b/firebase-firestore/CHANGELOG.md index 7daaf38a09c..8844874adf8 100644 --- a/firebase-firestore/CHANGELOG.md +++ b/firebase-firestore/CHANGELOG.md @@ -9,6 +9,9 @@ [#7388](//github.com/firebase/firebase-android-sdk/pull/7388) - [changed] Improve query performance by using an unsorted HashMap instead of a sorted TreeMap. [#7389](//github.com/firebase/firebase-android-sdk/pull/7389) +- [changed] Add `java.time.Instant` support to `DocumentSnapshot.toObject()`, + `DocumentReference.set()`, `DocumentReference.update()`, and similar. + [#6235](//github.com/firebase/firebase-android-sdk/pull/6235) # 26.0.1