From c4e14936413ba227b919d7018e04a843906f8651 Mon Sep 17 00:00:00 2001 From: Kim Jiun <40026920+KKimj@users.noreply.github.com> Date: Mon, 22 Jul 2024 18:56:23 +0800 Subject: [PATCH] feat: Update Timestamp Constructor with nanoseconds (#27) --- packages/dart_firebase_admin/CHANGELOG.md | 1 + .../src/google_cloud_firestore/timestamp.dart | 40 ++++++++++++++-- .../timestamp_test.dart | 47 +++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 packages/dart_firebase_admin/test/google_cloud_firestore/timestamp_test.dart diff --git a/packages/dart_firebase_admin/CHANGELOG.md b/packages/dart_firebase_admin/CHANGELOG.md index c96d2ca..20e5046 100644 --- a/packages/dart_firebase_admin/CHANGELOG.md +++ b/packages/dart_firebase_admin/CHANGELOG.md @@ -8,6 +8,7 @@ unused codes. - Fixes crash when updating users (thanks to @HeySreelal) - Marked various classes that cannot be extended as base/final. +- Added a default constructor on `Timestamp` (thanks to @KKimj) ## 0.3.1 diff --git a/packages/dart_firebase_admin/lib/src/google_cloud_firestore/timestamp.dart b/packages/dart_firebase_admin/lib/src/google_cloud_firestore/timestamp.dart index 3794f28..44cd7d4 100644 --- a/packages/dart_firebase_admin/lib/src/google_cloud_firestore/timestamp.dart +++ b/packages/dart_firebase_admin/lib/src/google_cloud_firestore/timestamp.dart @@ -14,9 +14,19 @@ String _toGoogleDateTime({required int seconds, required int nanoseconds}) { return '${formattedDate}Z'; } +/// A Timestamp represents a point in time independent of any time zone or calendar, +/// represented as seconds and fractions of seconds at nanosecond resolution in UTC +/// Epoch time. It is encoded using the Proleptic Gregorian Calendar which extends +/// the Gregorian calendar backwards to year one. It is encoded assuming all minutes +/// are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second table +/// is needed for interpretation. Range is from 0001-01-01T00:00:00Z to +/// 9999-12-31T23:59:59.999999999Z. By restricting to that range, we ensure that we +/// can convert to and from RFC 3339 date strings. +/// +/// For more information, see [the reference timestamp definition](https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto) @immutable final class Timestamp implements _Serializable { - Timestamp._({required this.seconds, required this.nanoseconds}) { + Timestamp({required this.seconds, required this.nanoseconds}) { const minSeconds = -62135596800; const maxSeconds = 253402300799; @@ -63,7 +73,7 @@ final class Timestamp implements _Serializable { /// Returns a new [Timestamp] representing the same point in time /// as the given date. factory Timestamp.fromDate(DateTime date) { - return Timestamp.fromMillis(date.millisecondsSinceEpoch); + return Timestamp.fromMicros(date.microsecondsSinceEpoch); } /// Creates a new timestamp from the given number of milliseconds. @@ -82,7 +92,28 @@ final class Timestamp implements _Serializable { factory Timestamp.fromMillis(int milliseconds) { final seconds = (milliseconds / 1000).floor(); final nanos = (milliseconds - seconds * 1000) * _msToNanos; - return Timestamp._(seconds: seconds, nanoseconds: nanos); + + return Timestamp(seconds: seconds, nanoseconds: nanos); + } + + /// Creates a new timestamp from the given number of microseconds. + /// + /// ```dart + /// final documentRef = firestore.doc('col/doc'); + /// + /// documentRef.set({ 'startTime': Timestamp.fromMicros(42) }); + /// ``` + /// + /// - [microseconds]: Number of microseconds since Unix epoch + /// 1970-01-01T00:00:00Z. + /// + /// Returns a new [Timestamp] representing the same point in time + /// as the given number of microseconds. + factory Timestamp.fromMicros(int microseconds) { + final seconds = (microseconds / 1000 / 1000).floor(); + final nanos = (microseconds - seconds * 1000 * 1000) * _usToNanos; + + return Timestamp(seconds: seconds, nanoseconds: nanos); } factory Timestamp._fromString(String timestampValue) { @@ -106,13 +137,14 @@ final class Timestamp implements _Serializable { ); } - return Timestamp._( + return Timestamp( seconds: date.millisecondsSinceEpoch ~/ 1000, nanoseconds: nanos, ); } static const _msToNanos = 1000000; + static const _usToNanos = 1000; final int seconds; final int nanoseconds; diff --git a/packages/dart_firebase_admin/test/google_cloud_firestore/timestamp_test.dart b/packages/dart_firebase_admin/test/google_cloud_firestore/timestamp_test.dart new file mode 100644 index 0000000..265c2e1 --- /dev/null +++ b/packages/dart_firebase_admin/test/google_cloud_firestore/timestamp_test.dart @@ -0,0 +1,47 @@ +import 'package:dart_firebase_admin/firestore.dart'; +import 'package:test/test.dart'; + +void main() { + group('Timestamp', () { + test('constructor', () { + final now = DateTime.now().toUtc(); + final seconds = now.millisecondsSinceEpoch ~/ 1000; + final nanoseconds = + (now.microsecondsSinceEpoch - seconds * 1000 * 1000) * 1000; + + expect( + Timestamp(seconds: seconds, nanoseconds: nanoseconds), + Timestamp.fromDate(now), + ); + }); + + test('fromDate constructor', () { + final now = DateTime.now().toUtc(); + final timestamp = Timestamp.fromDate(now); + + expect(timestamp.seconds, now.millisecondsSinceEpoch ~/ 1000); + }); + + test('fromMillis constructor', () { + final now = DateTime.now().toUtc(); + final timestamp = Timestamp.fromMillis(now.millisecondsSinceEpoch); + + expect(timestamp.seconds, now.millisecondsSinceEpoch ~/ 1000); + expect( + timestamp.nanoseconds, + (now.millisecondsSinceEpoch % 1000) * (1000 * 1000), + ); + }); + + test('fromMicros constructor', () { + final now = DateTime.now().toUtc(); + final timestamp = Timestamp.fromMicros(now.microsecondsSinceEpoch); + + expect(timestamp.seconds, now.microsecondsSinceEpoch ~/ (1000 * 1000)); + expect( + timestamp.nanoseconds, + (now.microsecondsSinceEpoch % (1000 * 1000)) * 1000, + ); + }); + }); +}