Skip to content

Commit fb10f03

Browse files
authored
feat: switch timestamp representation to int64 usec (#1332)
1 parent 496f52c commit fb10f03

File tree

6 files changed

+73
-28
lines changed

6 files changed

+73
-28
lines changed

src/bigquery.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,15 +2217,13 @@ export class BigQueryTimestamp {
22172217
if (/^\d{4}-\d{1,2}-\d{1,2}/.test(value)) {
22182218
pd = new PreciseDate(value);
22192219
} else {
2220-
const floatValue = Number.parseFloat(value);
2221-
if (!Number.isNaN(floatValue)) {
2222-
pd = this.fromFloatValue_(floatValue);
2223-
} else {
2224-
pd = new PreciseDate(value);
2225-
}
2220+
pd = new PreciseDate(BigInt(value) * BigInt(1000));
22262221
}
2222+
} else if (value) {
2223+
pd = new PreciseDate(BigInt(value) * BigInt(1000));
22272224
} else {
2228-
pd = this.fromFloatValue_(value);
2225+
// Nan or 0 - invalid dates
2226+
pd = new PreciseDate(value);
22292227
}
22302228
// to keep backward compatibility, only converts with microsecond
22312229
// precision if needed.
@@ -2235,15 +2233,6 @@ export class BigQueryTimestamp {
22352233
this.value = new Date(pd.getTime()).toJSON();
22362234
}
22372235
}
2238-
2239-
fromFloatValue_(value: number): PreciseDate {
2240-
const secs = Math.trunc(value);
2241-
// Timestamps in BigQuery have microsecond precision, so we must
2242-
// return a round number of microseconds.
2243-
const micros = Math.trunc((value - secs) * 1e6 + 0.5);
2244-
const pd = new PreciseDate([secs, micros * 1000]);
2245-
return pd;
2246-
}
22472236
}
22482237

22492238
/**

src/job.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,10 +529,10 @@ class Job extends Operation {
529529
typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
530530
const callback =
531531
typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
532-
533532
const qs = extend(
534533
{
535534
location: this.location,
535+
'formatOptions.useInt64Timestamp': true,
536536
},
537537
options
538538
);

src/table.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1848,10 +1848,17 @@ class Table extends ServiceObject {
18481848
callback!(null, rows, nextQuery, resp);
18491849
};
18501850

1851+
const qs = extend(
1852+
{
1853+
'formatOptions.useInt64Timestamp': true,
1854+
},
1855+
options
1856+
);
1857+
18511858
this.request(
18521859
{
18531860
uri: '/data',
1854-
qs: options,
1861+
qs,
18551862
},
18561863
(err, resp) => {
18571864
if (err) {
@@ -1860,7 +1867,7 @@ class Table extends ServiceObject {
18601867
}
18611868
let nextQuery: GetRowsOptions | null = null;
18621869
if (resp.pageToken) {
1863-
nextQuery = Object.assign({}, options, {
1870+
nextQuery = Object.assign({}, qs, {
18641871
pageToken: resp.pageToken,
18651872
});
18661873
}

test/bigquery.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,8 +850,10 @@ describe('BigQuery', () => {
850850
describe('timestamp', () => {
851851
const INPUT_STRING = '2016-12-06T12:00:00.000Z';
852852
const INPUT_STRING_MICROS = '2016-12-06T12:00:00.123456Z';
853+
const INPUT_STRING_NEGATIVE = '1969-12-25T00:00:00.000Z';
853854
const INPUT_DATE = new Date(INPUT_STRING);
854855
const INPUT_PRECISE_DATE = new PreciseDate(INPUT_STRING_MICROS);
856+
const INPUT_PRECISE_NEGATIVE_DATE = new PreciseDate(INPUT_STRING_NEGATIVE);
855857
const EXPECTED_VALUE = INPUT_DATE.toJSON();
856858
const EXPECTED_VALUE_MICROS = INPUT_PRECISE_DATE.toISOString();
857859

@@ -881,6 +883,26 @@ describe('BigQuery', () => {
881883
assert.strictEqual(timestamp.value, EXPECTED_VALUE);
882884
});
883885

886+
it('should accept a number in microseconds', () => {
887+
let ms = INPUT_PRECISE_DATE.valueOf(); // milliseconds
888+
let us = ms * 1000 + INPUT_PRECISE_DATE.getMicroseconds(); // microseconds
889+
let timestamp = bq.timestamp(us);
890+
assert.strictEqual(timestamp.value, EXPECTED_VALUE_MICROS);
891+
892+
let usStr = `${us}`;
893+
timestamp = bq.timestamp(usStr);
894+
assert.strictEqual(timestamp.value, EXPECTED_VALUE_MICROS);
895+
896+
ms = INPUT_PRECISE_NEGATIVE_DATE.valueOf();
897+
us = ms * 1000;
898+
timestamp = bq.timestamp(us);
899+
assert.strictEqual(timestamp.value, INPUT_STRING_NEGATIVE);
900+
901+
usStr = `${us}`;
902+
timestamp = bq.timestamp(usStr);
903+
assert.strictEqual(timestamp.value, INPUT_STRING_NEGATIVE);
904+
});
905+
884906
it('should accept a string with microseconds', () => {
885907
const timestamp = bq.timestamp(INPUT_STRING_MICROS);
886908
assert.strictEqual(timestamp.value, EXPECTED_VALUE_MICROS);

test/job.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,10 @@ describe('BigQuery/Job', () => {
238238

239239
it('should optionally accept options', done => {
240240
const options = {a: 'b'};
241-
const expectedOptions = Object.assign({location: undefined}, options);
241+
const expectedOptions = Object.assign(
242+
{location: undefined, 'formatOptions.useInt64Timestamp': true},
243+
options
244+
);
242245

243246
BIGQUERY.request = (reqOpts: DecorateRequestOptions) => {
244247
assert.deepStrictEqual(reqOpts.qs, expectedOptions);
@@ -252,7 +255,10 @@ describe('BigQuery/Job', () => {
252255
const job = new Job(BIGQUERY, JOB_ID, {location: LOCATION});
253256

254257
BIGQUERY.request = (reqOpts: DecorateRequestOptions) => {
255-
assert.deepStrictEqual(reqOpts.qs, {location: LOCATION});
258+
assert.deepStrictEqual(reqOpts.qs, {
259+
location: LOCATION,
260+
'formatOptions.useInt64Timestamp': true,
261+
});
256262
done();
257263
};
258264

@@ -261,7 +267,11 @@ describe('BigQuery/Job', () => {
261267

262268
it('should delete any cached jobs', done => {
263269
const options = {job: {}, a: 'b'};
264-
const expectedOptions = {location: undefined, a: 'b'};
270+
const expectedOptions = {
271+
location: undefined,
272+
a: 'b',
273+
'formatOptions.useInt64Timestamp': true,
274+
};
265275

266276
BIGQUERY.request = (reqOpts: DecorateRequestOptions) => {
267277
assert.deepStrictEqual(reqOpts.qs, expectedOptions);
@@ -340,7 +350,10 @@ describe('BigQuery/Job', () => {
340350
const mergedRows: Array<{}> = [];
341351

342352
const options = {wrapIntegers: true};
343-
const expectedOptions = Object.assign({location: undefined});
353+
const expectedOptions = Object.assign({
354+
location: undefined,
355+
'formatOptions.useInt64Timestamp': true,
356+
});
344357

345358
BIGQUERY.request = (reqOpts: DecorateRequestOptions) => {
346359
assert.deepStrictEqual(reqOpts.qs, expectedOptions);
@@ -368,7 +381,10 @@ describe('BigQuery/Job', () => {
368381
const mergedRows: Array<{}> = [];
369382

370383
const options = {parseJSON: true};
371-
const expectedOptions = Object.assign({location: undefined});
384+
const expectedOptions = Object.assign({
385+
location: undefined,
386+
'formatOptions.useInt64Timestamp': true,
387+
});
372388

373389
BIGQUERY.request = (reqOpts: DecorateRequestOptions) => {
374390
assert.deepStrictEqual(reqOpts.qs, expectedOptions);

test/table.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,7 +1979,10 @@ describe('BigQuery/Table', () => {
19791979

19801980
table.request = (reqOpts: DecorateRequestOptions, callback: Function) => {
19811981
assert.strictEqual(reqOpts.uri, '/data');
1982-
assert.strictEqual(reqOpts.qs, options);
1982+
assert.deepStrictEqual(reqOpts.qs, {
1983+
...options,
1984+
'formatOptions.useInt64Timestamp': true,
1985+
});
19831986
callback(null, {});
19841987
};
19851988

@@ -2129,14 +2132,18 @@ describe('BigQuery/Table', () => {
21292132
table.metadata = {schema: {}};
21302133

21312134
table.request = (reqOpts: DecorateRequestOptions, callback: Function) => {
2132-
callback(null, {pageToken});
2135+
callback(null, {
2136+
'formatOptions.useInt64Timestamp': true,
2137+
pageToken,
2138+
});
21332139
};
21342140

21352141
table.getRows(options, (err: Error, rows: {}, nextQuery: {}) => {
21362142
assert.ifError(err);
21372143
assert.deepStrictEqual(nextQuery, {
21382144
a: 'b',
21392145
c: 'd',
2146+
'formatOptions.useInt64Timestamp': true,
21402147
pageToken,
21412148
});
21422149
// Original object isn't affected.
@@ -2257,7 +2264,9 @@ describe('BigQuery/Table', () => {
22572264
const merged = [{name: 'stephen'}];
22582265

22592266
table.request = (reqOpts: DecorateRequestOptions, callback: Function) => {
2260-
assert.deepStrictEqual(reqOpts.qs, {});
2267+
assert.deepStrictEqual(reqOpts.qs, {
2268+
'formatOptions.useInt64Timestamp': true,
2269+
});
22612270
callback(null, {});
22622271
};
22632272

@@ -2279,7 +2288,9 @@ describe('BigQuery/Table', () => {
22792288
const merged = [{name: 'stephen'}];
22802289

22812290
table.request = (reqOpts: DecorateRequestOptions, callback: Function) => {
2282-
assert.deepStrictEqual(reqOpts.qs, {});
2291+
assert.deepStrictEqual(reqOpts.qs, {
2292+
'formatOptions.useInt64Timestamp': true,
2293+
});
22832294
callback(null, {});
22842295
};
22852296

0 commit comments

Comments
 (0)