From cb4b6d4fc5e988629ca0d12fe188c77a2a5544ae Mon Sep 17 00:00:00 2001
From: Jude Kwashie <judekwashie70@gmail.com>
Date: Thu, 13 Mar 2025 13:33:53 +0000
Subject: [PATCH 1/4] fix(cloud_firestore): correct nanoseconds calculation for
 pre-1970 dates

---
 .../lib/src/timestamp.dart                                | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/timestamp.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/timestamp.dart
index c29fef9d6e12..b975f4018d69 100644
--- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/timestamp.dart
+++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/timestamp.dart
@@ -40,8 +40,12 @@ class Timestamp implements Comparable<Timestamp> {
 
   /// Create a [Timestamp] fromMicrosecondsSinceEpoch
   factory Timestamp.fromMicrosecondsSinceEpoch(int microseconds) {
-    final int seconds = microseconds ~/ _kMillion;
-    final int nanoseconds = (microseconds - seconds * _kMillion) * _kThousand;
+    int seconds = microseconds ~/ _kMillion;
+    int nanoseconds = (microseconds - seconds * _kMillion) * _kThousand;
+    if (nanoseconds < 0) {
+      seconds -= 1;
+      nanoseconds += _kBillion;
+    }
     return Timestamp(seconds, nanoseconds);
   }
 

From 024e0f498426f1fd654c31510533837a6314ffe3 Mon Sep 17 00:00:00 2001
From: Jude Kwashie <judekwashie70@gmail.com>
Date: Thu, 13 Mar 2025 16:07:46 +0000
Subject: [PATCH 2/4] chore: add test to ensure Timestamp.fromDate handles
 pre-1970 dates correctly

---
 .../test/timestamp_test.dart                  | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/test/timestamp_test.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/test/timestamp_test.dart
index e631213a352e..bfd595095cd2 100644
--- a/packages/cloud_firestore/cloud_firestore_platform_interface/test/timestamp_test.dart
+++ b/packages/cloud_firestore/cloud_firestore_platform_interface/test/timestamp_test.dart
@@ -80,5 +80,24 @@ void main() {
 
       expect(epoch, equals(-9999999999));
     });
+
+    test('Timestamp should not throw for dates before 1970', () {
+      final dates = [
+        DateTime(1969, 06, 22, 0, 0, 0, 123),
+        DateTime(1969, 12, 31, 23, 59, 59, 999),
+        DateTime(1900, 01, 01, 12, 30, 45, 500),
+        DateTime(1800, 07, 04, 18, 15, 30, 250),
+        DateTime(0001, 01, 01, 00, 00, 00, 001),
+      ];
+
+      for (final date in dates) {
+        try {
+          final timestamp = Timestamp.fromDate(date);
+          expect(timestamp, isA<Timestamp>());
+        } catch (e) {
+          fail('Timestamp.fromDate threw an error: $e');
+        }
+      }
+    });
   });
 }

From 6484c796ea69ccdfdb2debf75c531e9a96277cd0 Mon Sep 17 00:00:00 2001
From: Jude Kwashie <judekwashie70@gmail.com>
Date: Thu, 13 Mar 2025 16:13:24 +0000
Subject: [PATCH 3/4] chore: add native implementation reference

---
 .../cloud_firestore_platform_interface/lib/src/timestamp.dart  | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/timestamp.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/timestamp.dart
index b975f4018d69..c14faf4dd3ab 100644
--- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/timestamp.dart
+++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/timestamp.dart
@@ -42,6 +42,9 @@ class Timestamp implements Comparable<Timestamp> {
   factory Timestamp.fromMicrosecondsSinceEpoch(int microseconds) {
     int seconds = microseconds ~/ _kMillion;
     int nanoseconds = (microseconds - seconds * _kMillion) * _kThousand;
+
+    // Matches implementation in Android SDK:
+    // https://github.com/firebase/firebase-android-sdk/blob/master/firebase-common/src/main/java/com/google/firebase/Timestamp.kt#L114-L121
     if (nanoseconds < 0) {
       seconds -= 1;
       nanoseconds += _kBillion;

From d3dca0a3e1c6a9013c4001722eb903de920ddb8e Mon Sep 17 00:00:00 2001
From: Jude Kwashie <judekwashie70@gmail.com>
Date: Fri, 14 Mar 2025 12:11:50 +0000
Subject: [PATCH 4/4] chore: add more tests

---
 .../example/integration_test/timestamp_e2e.dart | 17 +++++++++++++++++
 .../test/timestamp_test.dart                    | 10 ++++++++++
 2 files changed, 27 insertions(+)

diff --git a/packages/cloud_firestore/cloud_firestore/example/integration_test/timestamp_e2e.dart b/packages/cloud_firestore/cloud_firestore/example/integration_test/timestamp_e2e.dart
index c72564cf5d45..8c78853df295 100644
--- a/packages/cloud_firestore/cloud_firestore/example/integration_test/timestamp_e2e.dart
+++ b/packages/cloud_firestore/cloud_firestore/example/integration_test/timestamp_e2e.dart
@@ -53,5 +53,22 @@ void runTimestampTests() {
         equals(date.millisecondsSinceEpoch),
       );
     });
+
+    test('set pre-1970 $Timestamp and return', () async {
+      DocumentReference<Map<String, dynamic>> doc =
+          await initializeTest('timestamp');
+      final date = DateTime(1969, 06, 22, 0, 0, 0, 123);
+      final localTimestamp = Timestamp.fromDate(date);
+
+      await doc.set({'foo': localTimestamp});
+
+      DocumentSnapshot<Map<String, dynamic>> snapshot = await doc.get();
+      Timestamp retievedTimestamp = snapshot.data()!['foo'];
+      expect(retievedTimestamp, isA<Timestamp>());
+      expect(
+        retievedTimestamp,
+        equals(localTimestamp),
+      );
+    });
   });
 }
diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/test/timestamp_test.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/test/timestamp_test.dart
index bfd595095cd2..232d48bb8900 100644
--- a/packages/cloud_firestore/cloud_firestore_platform_interface/test/timestamp_test.dart
+++ b/packages/cloud_firestore/cloud_firestore_platform_interface/test/timestamp_test.dart
@@ -99,5 +99,15 @@ void main() {
         }
       }
     });
+
+    test(
+        'pre-1970 Timestamps should match the original DateTime after conversion',
+        () {
+      final date = DateTime(1969, 06, 22, 0, 0, 0, 123);
+      final timestamp = Timestamp.fromDate(date);
+      final timestampAsDateTime = timestamp.toDate();
+
+      expect(date, equals(timestampAsDateTime));
+    });
   });
 }