Skip to content

🐛 Cloud Firestore - microseconds are lost when implicitly converting DateTime to Timestamp #12102

Description

@kwill39

Bug report

Describe the bug
The microseconds of DateTime are lost when working with ToFirestore<R> for a withConverter for a CollectionReference being implicitly converted to Timestamp.

Steps to reproduce

Steps to reproduce the behavior:

  1. Tap the floating action button
  2. Observe terminal output with expected result and actual result
  3. Repeat as desired with different DateTime inputs

Expected behavior

DateTime should be converted without the loss of microseconds.

Sample project

This bug requires a Firebase project to reproduce. As such, the code needed to reproduce the problem is being provided in place of a repository URL. Anyone who would like to reproduce the bug is encouraged to create a new Firebase project in order to compile the following code sample. The code sample provided makes use of the Firestore emulator.

Note

The following code sample uses a custom object with ToFirestore and FromFirestore. There is a more concise version of the code sample, in a separate comment below, that reproduces the bug and does not involve a custom object.

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  FirebaseFirestore firestore = FirebaseFirestore.instance;
  firestore.useFirestoreEmulator('localhost', 8080);
  Settings settings = const Settings(persistenceEnabled: false);
  firestore.settings = settings;
  runApp(const MyApp());
}

class CustomDateTime {
  CustomDateTime(this.dateTime);

  final DateTime dateTime;

  static CustomDateTime fromFirestore(
    DocumentSnapshot<Map<String, dynamic>> snapshot,
    SnapshotOptions? options,
  ) {
    Timestamp timestamp = snapshot.data()!['date'] as Timestamp;
    DateTime date = timestamp.toDate().toLocal();
    return CustomDateTime(date);
  }

  static Map<String, Object?> toFirestore(
    CustomDateTime customDateTime,
    SetOptions? options,
  ) {
    return <String, Object?>{
      'date': customDateTime.dateTime,
    };
  }
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () async {
            int oneMicrosecond = 1;
            DateTime dateTime = DateTime(2023, 11, 1, 0, 0, 0, 0, oneMicrosecond);
            CustomDateTime customDateTime = CustomDateTime(dateTime);
            CollectionReference<CustomDateTime> collectionRef =
                FirebaseFirestore.instance
                    .collection('dates')
                    .withConverter<CustomDateTime>(
                      fromFirestore: CustomDateTime.fromFirestore,
                      toFirestore: CustomDateTime.toFirestore,
                    );
            DocumentReference docRef = await collectionRef.add(customDateTime);
            DocumentSnapshot<CustomDateTime> docSnap =
                await collectionRef.doc(docRef.id).get();
            print('Expected DateTime: $dateTime');
            print('Actual DateTime: ${docSnap.data()!.dateTime}');
          },
        ),
      ),
    );
  }
}

Additional context

This bug occurs while using either the emulator or a live Firebase project.

This bug does not occur when converting DateTime to a Timestamp beforehand: Timestamp.fromDate(customDateTime.dateTime). This can be seen by modifying the above example:

static Map<String, Object?> toFirestore(
  CustomDateTime customDateTime,
  SetOptions? options,
) {
  return <String, Object?>{
    'date': Timestamp.fromDate(customDateTime.dateTime),
  };
}

Example Inputs / Outputs

Expected DateTime: 2023-11-01 00:00:00.000001
. . .Actual DateTime: 2023-11-01 00:00:00.000

Expected DateTime: 2023-11-01 00:00:00.001001
. . .Actual DateTime: 2023-11-01 00:00:00.001

Expected DateTime: 2023-11-01 00:00:00.999999
. . .Actual DateTime: 2023-11-01 00:00:00.999

Expected DateTime: 2023-11-01 23:59:59.999999
. . .Actual DateTime: 2023-11-01 23:59:59.999

Expected DateTime: 2023-11-30 23:59:59.999999
. . .Actual DateTime: 2023-11-30 23:59:59.999


Flutter doctor

Run flutter doctor and paste the output below:

Click To Expand
[√] Flutter (Channel stable, 3.16.5, [redacted])
[√] [redacted]                        
[√] Android toolchain - develop for Android devices (Android SDK version 32.0.0)                  
[√] Chrome - develop for the web                                                
[√] Visual Studio - [redacted] (Visual Studio Community 2022 17.0.5)  
[√] Android Studio (version 2023.1)                                             
[√] IntelliJ IDEA Community Edition (version 2023.3)                            
[√] VS Code (version 1.85.1)                                                    
[√] Connected device ([redacted])                                              
[√] Network resources                                                           

• No issues found!

Flutter dependencies

Run flutter pub deps -- --style=compact and paste the output below:

Click To Expand
Dart SDK 3.2.3
Flutter SDK 3.16.5

dependencies:
- cloud_firestore 4.13.6
- firebase_core 2.24.2
- flutter 0.0.0 

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions