Skip to content

Commit

Permalink
feat(NODE-6031): add t and i to Timestamp (#704)
Browse files Browse the repository at this point in the history
  • Loading branch information
nbbeeken authored Jul 19, 2024
1 parent ede8357 commit b766d0f
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 4 deletions.
22 changes: 19 additions & 3 deletions src/timestamp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export interface TimestampExtended {
/**
* @public
* @category BSONType
*
* A special type for _internal_ MongoDB use and is **not** associated with the regular Date type.
*/
export class Timestamp extends LongWithoutOverridesClass {
get _bsontype(): 'Timestamp' {
Expand All @@ -36,6 +38,20 @@ export class Timestamp extends LongWithoutOverridesClass {

static readonly MAX_VALUE = Long.MAX_UNSIGNED_VALUE;

/**
* An incrementing ordinal for operations within a given second.
*/
get i(): number {
return this.low >>> 0;
}

/**
* A `time_t` value measuring seconds since the Unix epoch
*/
get t(): number {
return this.high >>> 0;
}

/**
* @param int - A 64-bit bigint representing the Timestamp.
*/
Expand Down Expand Up @@ -127,7 +143,7 @@ export class Timestamp extends LongWithoutOverridesClass {

/** @internal */
toExtendedJSON(): TimestampExtended {
return { $timestamp: { t: this.high >>> 0, i: this.low >>> 0 } };
return { $timestamp: { t: this.t, i: this.i } };
}

/** @internal */
Expand All @@ -144,8 +160,8 @@ export class Timestamp extends LongWithoutOverridesClass {

inspect(depth?: number, options?: unknown, inspect?: InspectFn): string {
inspect ??= defaultInspect;
const t = inspect(this.high >>> 0, options);
const i = inspect(this.low >>> 0, options);
const t = inspect(this.t, options);
const i = inspect(this.i, options);
return `new Timestamp({ t: ${t}, i: ${i} })`;
}
}
40 changes: 39 additions & 1 deletion test/node/timestamp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,37 @@ describe('Timestamp', () => {
});
});

describe('get i() and get t()', () => {
it('i returns lower bits', () => {
const l = new BSON.Long(1, 2);
const ts = new BSON.Timestamp(l);
expect(ts.i).to.equal(l.low);
});

it('t returns higher bits', () => {
const l = new BSON.Long(1, 2);
const ts = new BSON.Timestamp(l);
expect(ts.t).to.equal(l.high);
});

describe('when signed negative input is provided to the constructor', () => {
it('t and i return unsigned values', () => {
const l = new BSON.Long(-1, -2);
// Check the assumption that Long did NOT change the values to unsigned.
expect(l).to.have.property('low', -1);
expect(l).to.have.property('high', -2);

const ts = new BSON.Timestamp(l);
expect(ts).to.have.property('i', 0xffffffff); // -1 unsigned
expect(ts).to.have.property('t', 0xfffffffe); // -2 unsigned

// Timestamp is a subclass of Long, high and low do not change:
expect(ts).to.have.property('low', -1);
expect(ts).to.have.property('high', -2);
});
});
});

it('should always be an unsigned value', () => {
let bigIntInputs: Timestamp[] = [];
if (!__noBigInt__) {
Expand All @@ -23,7 +54,8 @@ describe('Timestamp', () => {
new BSON.Timestamp({ t: 0xffff_ffff, i: 0xffff_ffff }),
// @ts-expect-error We do not advertise support for Int32 in the constructor of Timestamp
// We do expect it to work so that round tripping the Int32 instance inside a Timestamp works
new BSON.Timestamp({ t: new BSON.Int32(0x7fff_ffff), i: new BSON.Int32(0x7fff_ffff) })
new BSON.Timestamp({ t: new BSON.Int32(0x7fff_ffff), i: new BSON.Int32(0x7fff_ffff) }),
new BSON.Timestamp(new BSON.Timestamp({ t: 0xffff_ffff, i: 0xffff_ffff }))
];

for (const timestamp of table) {
Expand Down Expand Up @@ -69,6 +101,12 @@ describe('Timestamp', () => {
expect(timestamp.toExtendedJSON()).to.deep.equal({ $timestamp: input });
});

it('accepts timestamp object as input', () => {
const input = new BSON.Timestamp({ t: 89, i: 144 });
const timestamp = new BSON.Timestamp(input);
expect(timestamp.toExtendedJSON()).to.deep.equal({ $timestamp: { t: input.t, i: input.i } });
});

it('accepts { t, i } object as input and coerce to integer', () => {
const input = { t: new BSON.Int32(89), i: new BSON.Int32(144) };
// @ts-expect-error We do not advertise support for Int32 in the constructor of Timestamp
Expand Down
3 changes: 3 additions & 0 deletions test/types/bson.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,6 @@ expectType<(depth?: number | undefined, options?: unknown, inspect?: InspectFn |
expectNotDeprecated(new ObjectId('foo'));
expectDeprecated(new ObjectId(42));
expectNotDeprecated(new ObjectId(42 as string | number));

// Timestamp accepts timestamp because constructor allows: {i:number, t:number}
new Timestamp(new Timestamp(0n))

0 comments on commit b766d0f

Please sign in to comment.