Skip to content

Commit ae34552

Browse files
committed
Fix timestamp 96's nsec to be unsignd
msgpack/msgpack#266
1 parent ebd1b9c commit ae34552

File tree

2 files changed

+32
-15
lines changed

2 files changed

+32
-15
lines changed

src/ExtensionCodec.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,29 @@ export function encodeTimestampFromTimeSpec({ sec, nsec }: TimeSpec): Uint8Array
3636
return rv;
3737
}
3838
} else {
39-
// timestamp 96 = { nsec32 (signed), sec64 (signed) }
39+
// timestamp 96 = { nsec32 (unsigned), sec64 (signed) }
4040
const rv = new Uint8Array(12);
4141
const view = new DataView(rv.buffer);
42-
view.setInt32(0, nsec);
42+
view.setUint32(0, nsec);
4343
encodeInt64(sec, view, 4);
4444
return rv;
4545
}
4646
}
4747

48+
export function encodeDateToTimeSpec(date: Date): TimeSpec {
49+
const time = date.getTime();
50+
const sec = time < 0 ? Math.ceil(time / 1000) : Math.floor(time / 1000);
51+
const nsec = (time - sec * 1000) * 1e6;
52+
return {
53+
sec: sec + Math.floor(nsec / 1e9),
54+
nsec: nsec - Math.floor(nsec / 1e9) * 1e9,
55+
};
56+
}
57+
4858
export const encodeTimestampExtension: ExtensionEncoderType = (object: unknown) => {
4959
if (isDate(object)) {
50-
const time = object.getTime();
51-
const sec = time < 0 ? Math.ceil(time / 1000) : Math.floor(time / 1000);
52-
const nsec = (time - sec * 1000) * 1e6;
53-
return encodeTimestampFromTimeSpec({ sec, nsec });
60+
const timeSpec = encodeDateToTimeSpec(object);
61+
return encodeTimestampFromTimeSpec(timeSpec);
5462
} else {
5563
return null;
5664
}
@@ -77,10 +85,10 @@ export const decodeTimestampExtension: ExtensionDecoderType = (data: Uint8Array)
7785
return new Date(sec * 1000 + nsec / 1e6);
7886
}
7987
case 12: {
80-
// timestamp 96 = { nsec32 (signed), sec64 (signed) }
88+
// timestamp 96 = { nsec32 (unsigned), sec64 (signed) }
8189
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
8290

83-
const nsec = view.getInt32(0);
91+
const nsec = view.getUint32(0);
8492
const sec = decodeInt64(data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11]);
8593

8694
return new Date(sec * 1000 + nsec / 1e6);

test/codec-timestamp.test.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import assert from "assert";
22
import util from "util";
33
import { encode, decode } from "../src";
4+
import { encodeDateToTimeSpec } from "../src/ExtensionCodec";
45

56
const TIME = 1556636810389;
67

@@ -17,13 +18,21 @@ const SPECS = {
1718
REGRESSION_1: new Date(1556799054803),
1819
} as Record<string, Date>;
1920

20-
describe("codec: timestamp 32/64/96", () => {
21-
for (const name of Object.keys(SPECS)) {
22-
const value = SPECS[name];
21+
describe("codec: timestamp", () => {
22+
context("timestamp 32/64/96", () => {
23+
for (const name of Object.keys(SPECS)) {
24+
const value = SPECS[name];
2325

24-
it(`encodes and decodes ${name} (${value.toISOString()})`, () => {
25-
const encoded = encode(value);
26-
assert.deepStrictEqual(decode(encoded), value, `encoded: ${util.inspect(Buffer.from(encoded))}`);
26+
it(`encodes and decodes ${name} (${value.toISOString()})`, () => {
27+
const encoded = encode(value);
28+
assert.deepStrictEqual(decode(encoded), value, `encoded: ${util.inspect(Buffer.from(encoded))}`);
29+
});
30+
}
31+
});
32+
33+
context("encodeDateToTimeSpec", () => {
34+
it("normalizes new Date(-1) to { sec: -1, nsec: 999000000 }", () => {
35+
assert.deepStrictEqual(encodeDateToTimeSpec(new Date(-1)), { sec: -1, nsec: 999000000 });
2736
});
28-
}
37+
});
2938
});

0 commit comments

Comments
 (0)