Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(NODE-6031): add t and i to Timestamp #704

Merged
merged 3 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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))