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

implement Timestamp96 #88

Merged
merged 2 commits into from
Dec 21, 2020
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
66 changes: 64 additions & 2 deletions lib/codecs/DateCodec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,45 @@ function encode (dt) {
var seconds = Math.floor(millis / 1000)
var nanos = (millis - seconds * 1000) * 1e6

if (nanos || seconds > 0xffffffff) {
if (seconds < 0 || seconds > 0x400000000) {
// Timestamp96
const encoded = Buffer.allocUnsafe(13)
encoded[0] = -1

encoded.writeUInt32BE(nanos, 1)

var hex = ''
if (seconds >= 0) {
const padhex = '0000000000000000'
hex = seconds.toString(16)
// add some padding
hex = padhex.slice(0, hex.length * -1) + hex
} else {
// encode seconds in 2's Complement 64Bit
// reverse sign
// keep all bits 0 and first 1 from right
// reverse all other bits
var bin = (seconds * -1).toString(2)
var i = bin.length - 1
while (bin[i] === '0') {
i--
}
bin = bin.slice(0, i).split('').map(function (bit) { return bit === '1' ? 0 : 1 }).join('') + bin.slice(i, bin.length)
// add some padding
const pad64 = '1111111111111111111111111111111111111111111111111111111111111111'
bin = pad64.slice(0, bin.length * -1) + bin
// convert to hex
bin.match(/.{1,8}/g).forEach(function (byte) {
byte = parseInt(byte, 2).toString(16)
if (byte.length === 1) {
byte = '0' + byte
}
hex += byte
})
}
encoded.write(hex, 5, 'hex')
return encoded
} else if (nanos || seconds > 0xffffffff) {
// Timestamp64
const encoded = Buffer.allocUnsafe(9)
encoded[0] = -1
Expand Down Expand Up @@ -55,7 +93,31 @@ function decode (buf) {
break

case 12:
throw new Error('timestamp 96 is not yet implemented')
// timestamp 96 stores the number of seconds and nanoseconds that have elapsed
// since 1970-01-01 00:00:00 UTC in 64-bit signed integer and 32-bit unsigned integer

// get seconds in hex
var hex = buf.toString('hex', 4, 12)
// check if seconds is a negative number
if (parseInt(buf.toString('hex', 4, 6), 16) & 0x80) {
// convert to binary
var bin = ''
const pad8 = '00000000'
hex.match(/.{1,2}/g).forEach(function (byte) {
byte = parseInt(byte, 16).toString(2)
byte = pad8.slice(0, byte.length * -1) + byte
bin += byte
})
// decode seconds from 2's Complement 64Bit
// reverse all bits
// reverse sign
// remove one
seconds = -1 * parseInt(bin.split('').map(function (bit) { return bit === '1' ? 0 : 1 }).join(''), 2) - 1
} else {
seconds = parseInt(hex, 16)
}

nanoseconds = buf.readUInt32BE(0)
}

var millis = (seconds * 1000) + Math.round(nanoseconds / 1E6)
Expand Down
43 changes: 43 additions & 0 deletions test/timestamps.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,46 @@ test('encoding/decoding timestamp 64', function (t) {

t.end()
})

test('encoding/decoding timestamp 96', function (t) {
var encoder = msgpack()
var timestamps = [
[new Date('0001-01-02T03:04:05.000000000Z'), [0xc7, 0x0c, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xf1, 0x88, 0x6f, 0x85, 0xa5]],
[new Date('1251-01-19T03:14:08.000000000Z'), [0xc7, 0x0c, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfa, 0xb7, 0xb2, 0xdf, 0x00]],
[new Date('1526-01-19T03:14:07.999000000Z'), [0xc7, 0x0c, 0xff, 0x3b, 0x8b, 0x87, 0xc0, 0xff, 0xff, 0xff, 0xfc, 0xbc, 0xf4, 0x34, 0x7f]],
[new Date('1920-02-07T06:28:16.000000000Z'), [0xc7, 0x0c, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa2, 0x23, 0xf0, 0x00]],
[new Date('1969-01-02T03:04:05.678000000Z'), [0xc7, 0x0c, 0xff, 0x28, 0x69, 0x75, 0x80, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x20, 0x49, 0x25]],
[new Date('2514-05-30T02:04:05.678000000Z'), [0xc7, 0x0c, 0xff, 0x28, 0x69, 0x75, 0x80, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x95]]
]

timestamps.forEach(function (testcase) {
var item = testcase[0]
var expected = testcase[1]

t.test('encoding ' + item.toString(), function (t) {
var buf = encoder.encode(item).slice()
t.equal(buf.length, expected.length, 'must have ' + expected.length + ' bytes')
t.equal(buf[0], 0xc7, 'must have the correct header')
t.equal(buf.readInt8(1), 12, 'must have the correct size')
t.equal(buf.readInt8(2), -1, 'must have the correct type') // Signed
for (var j = 3; j < buf.length; j++) {
t.equal(buf[j], expected[j], 'byte ' + (j - 3) + ' match')
}
t.end()
})

t.test('decoding ' + item, function (t) {
var buf = Buffer.from(expected)
var dt = encoder.decode(buf)
t.equal(dt.toString(), item.toString(), 'must decode correctly\nDecoded:\t' + dt * 1 + '\nExp:\t' + item * 1)
t.end()
})

t.test('mirror test ' + item, function (t) {
t.equal(encoder.decode(encoder.encode(item)) * 1, item * 1, 'must stay the same')
t.end()
})
})

t.end()
})