Firestore dates written as strings, not Timestamps

I am updating our Firestore collection date fields and noticing that the dates are being written as ISO strings and not a date. What I expect to see is that a Timestamp value is stored in Firestore.

Example code being used:

const doc = {
  lastUpdatedAt = new Date(),
}

updateCollection.trigger({
  additionalScope: {
    collectionId: 'abcd',
    doc,
  }
})

The collection document is updated with the value but the date written is an ISO String value. When using Retool, what is the correct way to write a date to Firestore so it stores a Timestamp value?

written as:
image

Expect to see:
image

Note: these are sample I pulled from our collections, not the actual dates/fields in the example above

Hey @brettski! You’ll need to pass that as {{ moment() }} and it should work!

Hi @justin How would I do that form a JavaScript query?

if I do

const doc = {
  lastUpdatedAt = moment();
} 

The update fails with an error in console:

DOMException: Failed to execute 'postMessage' on 'Window': ...

FYI, doesn't throw failure in trigger only in the console and doesn't seem to fire trigger at all

DOMException: Failed to execute 'postMessage' on 'Window': function(t){var e=t%10,r=1===T(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+r} could not be cloned.
    at https://retool-edge.com/sandbox.171015831ecec319aab8.js:1:152790
    at new Promise (<anonymous>)
    at Object.trigger (https://retool-edge.com/sandbox.171015831ecec319aab8.js:1:152686)
    at eval (eval at <anonymous> (https://retool-edge.com/sandbox.171015831ecec319aab8.js:1:156796), <anonymous>:35:20)
    at Array.forEach (<anonymous>)
    at eval (eval at <anonymous> (https://retool-edge.com/sandbox.171015831ecec319aab8.js:1:156796), <anonymous>:8:11)

Hm, weird. I actually ran your original example with new Date() and it’s working fine for me. What does your updateCollection query look like?

In the Firebase query, the value is:
{{doc}}

Can you send a screenshot? I need more info on what kind of query it is, etc.

Ah, I see. It looks like the date object is getting stringified when it passes through the trigger - in the local repro I tried, I had it set up to define the object in the query itself, like so:

Is there any way you can break out the lastUpdatedAt field to be defined in the query itself? By combining it with the doc object somehow? It seems like if you pass the new Date() object in the Firebase query instead of the JS query, it should work.

Yeah, I thought of that thinking the same thing was happening. Pretty hacky as I may update one of two date fields or both. So the query may not always be the same. Funny that’s twice this week JSON not having a date type bit me.

So what is the way to take a Timestamp you receive from Firestore and write it back? Do you simply write the Timestamp object back to the update query, or does it need to be converted to a date first?

Another strange thing here. So on my test record I created a Timestamp field. When I read that field and dump it to the console, it is a string, not a Timestamp object. I thought, based on your August 13th email that Timestamps should come back as an object.

Yep, agreed it's hacky - we're still trying to figure out the right way to pass these to Firebase without turning them into strings. We should definitely support this if it's coming via a JS query to - I'll talk to the team and see what we can do here.

Re: writing back - so you can write back w/ an object that has _seconds and _nanoseconds but it looks like Firebase is just making that an object. If you want it to be a timestamp field it needs to be converted to a date first.

Re: timestamp / email, you are right - we ended up reverting back to the old format (string) to maintain backwards compatibility. I know this is confusing (and we apologize :frowning: ) - adding a note into our docs mentioning it today.

Sorry again about this @brettski - def not an ideal experience and we'll see what we can do to improve it.

It’s fine that you converted back, but please, please send an updated email that you did because everybody’s stuff was breaking!

Thank you for the help today and have a wonderful weekend.

Will there be a fix/change to update firebase with a timestamp if you pass a timestamp from a js query to a firebase query?

For example, right now, I need to pass an object from a js query to firebase, which then updates/adds a document in firebase.

Code example:
jsQuery

jsObject: {
  items: {
    ...
    timeUpdated: new Date()
    ...
  }
  timeCreated: new Date()
}

firebaseQuery value:

{{jsQuery.data}}

So this previously worked before the changes and everything that was a timestamp updated as a timestamp in firebase. With the recent changes, I had to add a workaround that was mentioned here to convert the timestamp from the jsObject in the firebaseQuery:

firebaseQuery value updated:

{
  items: {{itemsObject.data.items}}
  timeCreated: {{new Date(itemsObject.data.timeCreated)}}
}

This workaround works for timeCreated, however, I cannot set the items object timestamps with a “new Date()” because for my application, the items object is dynamic and the size of object is unknown until the time of triggering the queries.

Hey @jeff_melon! Yep, we’re working on fixing this this week

Any updates on this one? I have an existing date that I don’t want changed in any way. I want FireStore to continue to see it as type Timestamp. Thanks!

Hello Retool Team!

Poking my head up here to see if there are solutions around this coming down the pike.

Thank you,
Brett

Any news on this? This bit us hard on a week we were supposed to go live.

The value will be stored as a Firebase timestamp if you send it a date object, here is an example using moment:


Hey @ben thanks for the information. Yes, I know about this workaround, the biggest issue is that is is really rare that the date is the only thing being updated, most often it is an entire object being sent to be added or updated, which gets stringified and the date is stored as a string. There is currently know way to get the date as a Date when it's part of an object.

I just thought of this (and not thought through at all), but how about an option to JSON.parse a value being used or the ability to pass it through a transformer first? I am sure there are caveats there too.

Thank you,

Brett

Sorry for the delay!

The issue is that we stringify the items passed into the additionalScope, and that even when you JSON.parse() on the other side (in this case within the Firebase query), the value does not get converted back into a Moment object (it's just trying to parse a string). Instead, we have to use Moment within the Firebase query again, to convert the string into an object again.

So if we have our JS query:

Then within our Firebase query, we parse it back to an object:

Which then gets stored in FB as a Timestamp!