Suresh
Suresh

Reputation: 5987

Flutter app error - type 'Timestamp' is not a subtype of type 'DateTime'

I'm fetching data cloud firestore & trying to show in my app by using the following piece of code.

new Text(timeago.format(document.data['tripDoc']['docCreatedOn'])),

I'm using timeago dart package to format that. But, after making updating to latest cloud firestore plugin I'm getting this error -

Another exception was thrown: type 'Timestamp' is not a subtype of type 'DateTime'

Can't able to understand how to parse this 'TimeStamp' object to 'DateTime'. Because timeago plugin need data in DateTime object format.

Upvotes: 40

Views: 50315

Answers (22)

Barzi Yassin
Barzi Yassin

Reputation: 186

Firebase cloud_firestore has changed their mechanism of storing DateTime object value in a doc field, in previous mechanism datetime.millisecondsSinceEpoch stores int in firestore, but now it will be stored as date, then you need a logic to handle them.

To overcome the obstacles during data transmission you can write toMap() and fromMap() methods as following:

class YourModel {
  String? firstName;
  String? phoneNumber;
  DateTime? createdAt;
  YourModel({
    this.firstName,
    this.phoneNumber,
    this.createdAt,
  });

  Map<String, dynamic> toMap() {
    return <String, dynamic>{
      'firstName': firstName,
      'phoneNumber': phoneNumber,
      'createdAt': createdAt?.millisecondsSinceEpoch,
    };
  }

  factory YourModel.fromMap(Map<String, dynamic> map) {
    return YourModel(
      firstName: map['firstName'] != null ? map['firstName'] as String : null,
      phoneNumber:
          map['phoneNumber'] != null ? map['phoneNumber'] as String : null,
      createdAt: map['createdAt'] != null
          // ? DateTime.fromMillisecondsSinceEpoch(map['createdAt'] as int) // old technique, fixed below
          ? (map['createdAt'] is Timestamp
              ? (map['createdAt'] as Timestamp).toDate()
              : DateTime.fromMillisecondsSinceEpoch(map['createdAt'] as int))
          : null,
    );
  }
}

Upvotes: 0

Muhammad Adil Mehmood
Muhammad Adil Mehmood

Reputation: 51

This solution worked for me:

DateTime convertTimestampMapToDateTime(dynamic date) {
final createdAt = date;
DateTime parsedCreatedAt;

if (createdAt is Map) {
  parsedCreatedAt = Timestamp(
    createdAt['_seconds'] as int,
    createdAt['_nanoseconds'] as int,
  ).toDate();
}
return parsedCreatedAt;

}

Store your timestamp Map from Firestore in the createdAt variable and then parsedCreatedAt will have your timestamp in date format. you can then use it as per your requirements.

Upvotes: 0

krishnaacharyaa
krishnaacharyaa

Reputation: 24910

There are various ways to convert TimeStamp to DateTime which differs based on the scenario. I have tried to gather all the possible ways to convert Firebase timeStamp to DateTime. Pick which works for you.


Conversion of Firebase timestamp to DateTime:

  1. document['timeStamp'].toDate()
    
  2. (document["timeStamp"] as Timestamp).toDate()
    
  3. DateTime.fromMillisecondsSinceEpoch(document['timeStamp'].millisecondsSinceEpoch);
    
  4. Timestamp.fromMillisecondsSinceEpoch(document['timeStamp'].millisecondsSinceEpoch).toDate();
    
  5. If timeStamp is in microseconds use:

    DateTime.fromMicrosecondsSinceEpoch(timestamp * 1000000);
    
  6. If timeStamp is in milliseconds use:

    DateTime.fromMillisecondsSinceEpoch(timestamp * 1000);
    
  7. Add the following function in your dart file.

     String formatTimestamp(Timestamp timestamp) {
       var format = new DateFormat('yyyy-MM-dd'); // <- use skeleton here
       return format.format(timestamp.toDate());
     }
    

    call it as formatTimestamp(document['timestamp'])

Upvotes: 1

Ahmed Nabil
Ahmed Nabil

Reputation: 161

i had the same error because date is inserted as TimeStamp type in Firebase Firestore and i was trying to parse it as if it was Date type

this was my model that got me the error when trying to get data from firestore

    class Post {
  String title;
  String body;
  String teacherName;
  DateTime date;

  Post({
    required this.title,
    required this.body,
    required this.teacherName,
    required this.date,
  });

  factory Post.fromJson(Map<String, dynamic> json) => Post(
        title: json["title"],
        body: json["body"],
        teacherName: json["teacher"],
        date: DateTime.parse(json["date"]),
      );

  Map<String, dynamic> toJson() => {
        "title": title,
        "body": body,
        "teacher": teacherName,
        "date": date,
      };
}

i discovered that firestore has toDate() method in TimeStamp() that transform timestamp to Datetime

so in my code in toJson method i parsed the date field as Timestamp then transform it as DateTime

factory Post.fromJson(Map<String, dynamic> json) => Post(
        title: json["title"],
        body: json["body"],
        teacherName: json["teacher"],
        date: (json["date"] as Timestamp).toDate(),
      );

Upvotes: 3

Ramy Wahid
Ramy Wahid

Reputation: 443

if you using firestorm and getting (QuerySnapshot)

i use this line to get date from firestore:

postDate: doc.data().toString().contains('postDate') ? doc.get('postDate') :  DateTime.now()

in this method:

List<AdModel?>? _adModelListFromQuerySnapshot(QuerySnapshot snapshot) {
    try {
      return snapshot.docs.map((doc) {
        return PostModel(
       title: doc.data().toString().contains('title') ? doc.get('title') :'', 
       description: doc.data().toString().contains('description') ?doc.get('description') :  '',
       adDate: doc.data().toString().contains('adDate') ? doc.get('adDate') :  DateTime.now(),
        );
      }).toList();
    } catch (error) {
      print(' Show ${error.toString()}');
      return null;
    }
  }

This is my Model

PostModel {
DateTime? postDate;

PostModel({this.postDate}); 
}

I wish this is helpful

Upvotes: 0

ViKi Vyas
ViKi Vyas

Reputation: 741

Following function will take care of all.

DateTime vikiDate(dynamic dateValue) {
  if (dateValue is DateTime) {
    return dateValue;
  } else if (dateValue is String) {
    return DateTime.parse(dateValue);
  }
  else if (dateValue is Timestamp) {
    return dateValue.toDate();
  }
  else{
    return null;
  }
}

Just use it as

DateTime acceptedAt = vikiDate(json['accepted_at']);

Upvotes: 2

Birch
Birch

Reputation: 217

For the record, if you're using freezed, this worked for me:

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';

part 'activity_card_model.freezed.dart';
part 'activity_card_model.g.dart';

DateTime _createdAtFromJson(Timestamp timestamp) => timestamp.toDate();
Timestamp _createdAtToJson(DateTime date) => Timestamp.fromDate(date);
DateTime _updatedAtFromJson(Timestamp timestamp) => timestamp.toDate();
Timestamp _updatedAtToJson(DateTime date) => Timestamp.fromDate(date);

@freezed
abstract class ActivityCard implements _$ActivityCard {
  const ActivityCard._();

  const factory ActivityCard({
    @Default(true) bool active,
    @Default('other') String category,
    @JsonKey(name: 'created_at', fromJson: _createdAtFromJson, toJson: _createdAtToJson)
        required DateTime createdAt,
    required String description,
    required String heading,
    String? id,
    @JsonKey(name: 'image_name') @Default('') String imageName,
    @JsonKey(name: 'img_src') @Default('') String imageURL,
    @JsonKey(name: 'link') String? linkURL,
    @JsonKey(name: 'link_description') String? linkDescription,
    String? subheading,
    @JsonKey(name: 'updated_at', fromJson: _updatedAtFromJson, toJson: _updatedAtToJson)
        required DateTime updatedAt,
  }) = _ActivityCard;

  factory ActivityCard.fromFirestore(DocumentSnapshot doc) {
    Map<String, dynamic> json = doc.data as Map<String, dynamic>;
    return ActivityCard.fromJson(json);
  }

  factory ActivityCard.fromJson(Map<String, dynamic> json) =>
      _$ActivityCardFromJson(json);

  factory ActivityCard.fromDocument(
      DocumentSnapshot<Map<String, dynamic>> doc) {
    final data = doc.data()!;
    return ActivityCard.fromJson(data).copyWith(id: doc.id);
  }

  Map<String, dynamic> toDocument() => toJson()..remove('id');
}

Upvotes: -1

BC TUBE
BC TUBE

Reputation: 766

I made changes in my model class for that error.

from Backend, I'm getting this response

"reported_date": "2021-02-19T05:45:57.434150Z",

change from

reportedDate = json['reported_date']

to

reportedDate = json['reported_date'] == null ? null : DateTime.parse(json['reported_date'] as String);

This solves my problem.

After parsing you can use any formate to read about formates

Below some of the example, I'm testing for reference

///return in 12:08 PM format
static String returnTime(DateTime dt) {
  final DateFormat formatter = DateFormat('h:mm a');
  String time = formatter.format(dt);
  return time;
}

///return in Thu, Feb 18 format
String returnDate(DateTime dt) {
  final DateFormat formatter = DateFormat('EEE, MMM d');
  String date = formatter.format(dt);
  return date;
}

///return day (Thu, Feb 18, will return 18)
String standaloneDay(DateTime dt) {
  final DateFormat formatter = DateFormat('c');
  String date = formatter.format(dt);
  return date;
}

Upvotes: 0

sultanmyrza
sultanmyrza

Reputation: 5392

It is irrelavant but for those who use Timestamp (cloud_firestore) instead of DateTime you probably will get

<String, dynamic>' is not a subtype of type 'Timestamp'

class MyUser {
  String uid;
  String email;
  Timestamp firstJoined;
  
  MyUser.fromJson(Map<String, dynamic> data) {
    this.uid = data['uid'] ?? '';
    this.email = data['email'] ?? '';
    // PARSE FIRESTORE TIMESTAMP
    if (data['firstJoined'] != null) {
        this.firstJoined = Timestamp(
            data['firstJoined']['_seconds'],
            data['firstJoined']['_nanoseconds'],
        );
    } else {
        this.firstJoined=null;
    }
  }
}

Upvotes: 0

Junsu Lee
Junsu Lee

Reputation: 1541

If you use JsonSerializable, Use JsonConverter

class TimestampConverter implements JsonConverter<DateTime, Timestamp> {
  const TimestampConverter();

  @override
  DateTime fromJson(Timestamp timestamp) {
    return timestamp.toDate();
  }

  @override
  Timestamp toJson(DateTime date) => Timestamp.fromDate(date);
}

@JsonSerializable()
class User{
  final String id;
  @TimestampConverter()
  final DateTime timeCreated;

  User([this.id, this.timeCreated]);

  factory User.fromSnapshot(DocumentSnapshot documentSnapshot) =>
      _$UserFromJson(
          documentSnapshot.data..["_id"] = documentSnapshot.documentID);

  Map<String, dynamic> toJson() => _$UserToJson(this)..remove("_id");
}

Upvotes: 13

Elon Gomes Vieira
Elon Gomes Vieira

Reputation: 447

Try document.data["data"].microsecondsSinceEpoch

This is working for me:-

User.fromDocument(DocumentSnapshot document){
dataNasc = DateTime.fromMicrosecondsSinceEpoch(document.data["data"].microsecondsSinceEpoch);
}

Upvotes: 2

Sverrir Torfason
Sverrir Torfason

Reputation: 1

This could also be an error because in the database or firebase there is some type that is missing or different name on

Upvotes: 0

Richard K
Richard K

Reputation: 43

The Cloud Firebase timestamp field type has the structure:

Timestamp (Timestamp(seconds=1561186800, nanoseconds=0))
hashCode:423768083
microsecondsSinceEpoch:1561186800000000
millisecondsSinceEpoch:1561186800000
nanoseconds:0
runtimeType:Type (Timestamp)
seconds:1561186800
_seconds:1561186800
_nanoseconds:0

So you can use either micro- or milliseconds:

DateTime.fromMillisecondsSinceEpoch(data['tripDoc']['docCreatedOn'].millisecondsSinceEpoch)

or

DateTime.fromMicrosecondsSinceEpoch(data['tripDoc']['docCreatedOn'].microsecondsSinceEpoch)

Upvotes: 1

Monu
Monu

Reputation: 714

add toDate() method .It will work

DateTime dateTime = documents[i].data["duedate"].toDate();

Upvotes: 27

EQuimper
EQuimper

Reputation: 5929

Ios and Android will not receive same type. Ios receive the timestamps as TimeStamp and Android receive it as DateTime already. So for fixing this issue I just created this little function. This will return a DateTime and let use format it etc.

import 'dart:io';

import 'package:cloud_firestore/cloud_firestore.dart';

DateTime parseTime(dynamic date) {
  return Platform.isIOS ? (date as Timestamp).toDate() : (date as DateTime);
}

Upvotes: 12

blaneyneil
blaneyneil

Reputation: 3222

Firestore is returning a Timestamp object, which consists of seconds and nanoseconds. Oddly, on iOS you can indeed just use a .toDate() and it works. But that breaks on Android as toDate() is not a method. So you can do a platform check if you want, but the universal solution is to use Firestore's Timestamp:

import 'package:cloud_firestore/cloud_firestore.dart';

DateTime _convertStamp(Timestamp _stamp) {

  if (_stamp != null) {

    return Timestamp(_stamp.seconds, _stamp.nanoseconds).toDate();

    /*
    if (Platform.isIOS) {
      return _stamp.toDate();
    } else {
      return Timestamp(_stamp.seconds, _stamp.nanoseconds).toDate();
    }
    */

  } else {
    return null;
  }
}

and then pass your model to it:

  SomeModel.fromJson(Map<String, dynamic> parsedJson) {
    updatedAt = _convertStamp(parsedJson['updatedAt']);
  }

Upvotes: 1

Harsha pulikollu
Harsha pulikollu

Reputation: 2436

You can try this..

timeago.format(DateTime.tryParse(timestamp))

like in yours it will be

  timeago.format(DateTime.tryParse(document.data['tripDoc']['docCreatedOn']))

Upvotes: 0

kbrmys
kbrmys

Reputation: 61

For some funny reason, I can't use toDate() on Android. Have to use it for iOS. So I'm forced to use a platform check like this:

Theme.of(context).platform == TargetPlatform.iOS
  ? DateFormat('dd MMM kk:mm').format(document['timestamp'].toDate())
  : DateFormat('dd MMM kk:mm').format(document['timestamp'])

Upvotes: 0

Jawand Singh
Jawand Singh

Reputation: 2036

I think this is more reliable

DateTime updateDateTime = DateTime.fromMillisecondsSinceEpoch(
        map['updatedatetime'].millisecondsSinceEpoch);

Upvotes: 6

Suresh
Suresh

Reputation: 5987

.toDate() worked for me. Now the modified code is -

new Text(timeago.format(document.data['tripDoc']['docCreatedOn'].toDate()))

Hope, it'll help someone.

Upvotes: 64

Dinesh Balasubramanian
Dinesh Balasubramanian

Reputation: 21728

var date = DateTime.fromMillisecondsSinceEpoch(timestamp)

Upvotes: 0

Shady Aziza
Shady Aziza

Reputation: 53317

DateTime.fromMillisecondsSinceEpoch(timeStamp);
DateTime.fromMicrosecondsSinceEpoch(timeStamp);

Upvotes: 2

Related Questions