Skip to content

Commit 3eb29cb

Browse files
authored
Serialize Timestamps as string (per proto3 JSON format). (#2249)
* Serialize Timestamps as string (per proto3 JSON format). * [AUTOMATED]: Prettier Code Styling * Add an extra test for string format. * Update changelog.
1 parent dbe4086 commit 3eb29cb

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

packages/firestore/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
- [fixed] Fixed a regression that caused queries with nested field filters to
33
crash the client if the field was not present in the local copy of the
44
document.
5+
- [fixed] Fixed an issue that caused requests with timestamps to Firestore
6+
Emulator to fail.
57

68
# 1.5.0
79
- [feature] Added a `Firestore.waitForPendingWrites()` method that

packages/firestore/src/remote/serializer.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,11 +207,24 @@ export class JsonProtoSerializer {
207207
* to actually return a Timestamp proto.
208208
*/
209209
private toTimestamp(timestamp: Timestamp): string {
210-
return {
211-
seconds: '' + timestamp.seconds,
212-
nanos: timestamp.nanoseconds
213-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
214-
} as any;
210+
if (this.options.useProto3Json) {
211+
// Serialize to ISO-8601 date format, but with full nano resolution.
212+
// Since JS Date has only millis, let's only use it for the seconds and
213+
// then manually add the fractions to the end.
214+
const jsDateStr = new Date(timestamp.seconds * 1000).toISOString();
215+
// Remove .xxx frac part and Z in the end.
216+
const strUntilSeconds = jsDateStr.replace(/\.\d*/, '').replace('Z', '');
217+
// Pad the fraction out to 9 digits (nanos).
218+
const nanoStr = ('000000000' + timestamp.nanoseconds).slice(-9);
219+
220+
return `${strUntilSeconds}.${nanoStr}Z`;
221+
} else {
222+
return {
223+
seconds: '' + timestamp.seconds,
224+
nanos: timestamp.nanoseconds
225+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
226+
} as any;
227+
}
215228
}
216229

217230
private fromTimestamp(date: string | TimestampProto): Timestamp {

packages/firestore/test/unit/remote/node/serializer.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,38 @@ describe('Serializer', () => {
300300
);
301301
});
302302

303+
it('converts TimestampValue to string (useProto3Json=true)', () => {
304+
expect(
305+
proto3JsonSerializer.toValue(
306+
new fieldValue.TimestampValue(new Timestamp(1488872578, 916123456))
307+
)
308+
).to.deep.equal({ timestampValue: '2017-03-07T07:42:58.916123456Z' });
309+
310+
expect(
311+
proto3JsonSerializer.toValue(
312+
new fieldValue.TimestampValue(new Timestamp(1488872578, 916123000))
313+
)
314+
).to.deep.equal({ timestampValue: '2017-03-07T07:42:58.916123000Z' });
315+
316+
expect(
317+
proto3JsonSerializer.toValue(
318+
new fieldValue.TimestampValue(new Timestamp(1488872578, 916000000))
319+
)
320+
).to.deep.equal({ timestampValue: '2017-03-07T07:42:58.916000000Z' });
321+
322+
expect(
323+
proto3JsonSerializer.toValue(
324+
new fieldValue.TimestampValue(new Timestamp(1488872578, 916000))
325+
)
326+
).to.deep.equal({ timestampValue: '2017-03-07T07:42:58.000916000Z' });
327+
328+
expect(
329+
proto3JsonSerializer.toValue(
330+
new fieldValue.TimestampValue(new Timestamp(1488872578, 0))
331+
)
332+
).to.deep.equal({ timestampValue: '2017-03-07T07:42:58.000000000Z' });
333+
});
334+
303335
it('converts GeoPointValue', () => {
304336
const example = new GeoPoint(1.23, 4.56);
305337
const expected = {

0 commit comments

Comments
 (0)