From 869bbafe3790d6d6f21a1f538549a1f336d6f918 Mon Sep 17 00:00:00 2001 From: Nicole Zhu <69952136+nicoleczhu@users.noreply.github.com> Date: Mon, 9 Nov 2020 09:10:51 -0800 Subject: [PATCH] feat: accept entry.timestamp string input in RFC3339 format (#937) * feat: accept entry.timestamp string input in RFC3339 format * style: use Number instead of parseInt --- src/entry.ts | 14 +++++++++++++- test/entry.ts | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/entry.ts b/src/entry.ts index ccbaf06d..c993e291 100644 --- a/src/entry.ts +++ b/src/entry.ts @@ -24,7 +24,7 @@ import {objToStruct, structToObj} from './common'; const eventId = new EventId(); -export type Timestamp = google.protobuf.ITimestamp | Date; +export type Timestamp = google.protobuf.ITimestamp | Date | string; export type LogSeverity = google.logging.type.LogSeverity | string; export type LogEntry = Merge< google.logging.v2.ILogEntry, @@ -152,6 +152,7 @@ class Entry { */ toJSON(options: ToJsonOptions = {}) { const entry = (extend(true, {}, this.metadata) as {}) as EntryJson; + // Format log message if (is.object(this.data)) { entry.jsonPayload = objToStruct(this.data, { removeCircular: !!options.removeCircular, @@ -160,6 +161,7 @@ class Entry { } else if (is.string(this.data)) { entry.textPayload = this.data; } + // Format log timestamp if (is.date(entry.timestamp)) { const seconds = (entry.timestamp as Date).getTime() / 1000; const secondsRounded = Math.floor(seconds); @@ -167,6 +169,16 @@ class Entry { seconds: secondsRounded, nanos: Math.floor((seconds - secondsRounded) * 1e9), }; + } else if (is.string(entry.timestamp)) { + // Convert RFC3339 "Zulu" timestamp into a format that can be parsed to Date + const zuluTime = entry.timestamp as string; + const ms = Date.parse(zuluTime.split(/[.,Z]/)[0] + 'Z'); + const reNano = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.(\d{0,9})Z$/; + const nanoSecs = zuluTime.match(reNano)?.[1]; + entry.timestamp = { + seconds: ms ? Math.floor(ms / 1000) : 0, + nanos: nanoSecs ? Number(nanoSecs.padEnd(9, '0')) : 0, + }; } return entry; } diff --git a/test/entry.ts b/test/entry.ts index e9888495..1d7ebcb9 100644 --- a/test/entry.ts +++ b/test/entry.ts @@ -212,7 +212,7 @@ describe('Entry', () => { assert.strictEqual(json.textPayload, entry.data); }); - it('should convert a date', () => { + it('should convert a date timestamp', () => { const date = new Date(); entry.metadata.timestamp = date as entryTypes.Timestamp; const json = entry.toJSON(); @@ -223,5 +223,39 @@ describe('Entry', () => { nanos: Math.floor((seconds - secondsRounded) * 1e9), }); }); + + it('should convert a string timestamp', () => { + const tests = [ + { + inputTime: '2020-01-01T00:00:00.11Z', + expectedSeconds: 1577836800, + expectedNanos: 110000000, + }, + { + inputTime: '2020-01-01T00:00:00Z', + expectedSeconds: 1577836800, + expectedNanos: 0, + }, + { + inputTime: '2020-01-01T00:00:00.999999999Z', + expectedSeconds: 1577836800, + expectedNanos: 999999999, + }, + { + inputTime: 'invalid timestamp string', + expectedSeconds: 0, + expectedNanos: 0, + }, + ]; + + for (const test of tests) { + entry.metadata.timestamp = test.inputTime; + const json = entry.toJSON(); + assert.deepStrictEqual(json.timestamp, { + seconds: test.expectedSeconds, + nanos: test.expectedNanos, + }); + } + }); }); });