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

Viewed 35303

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.

19 Answers

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

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

Hope, it'll help someone.

add toDate() method .It will work

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

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);
}

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");
}

I think this is more reliable

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

Try document.data["data"].microsecondsSinceEpoch

This is working for me:-

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

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']);
  }

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)

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(),
      );

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'])

You can try this..

timeago.format(DateTime.tryParse(timestamp))

like in yours it will be

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

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

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;
    }
  }
}

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;
}

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']);

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

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');
}
Related