tick-time
Version:
Provides methods to create tick count similar to what is available in .NET
116 lines (103 loc) • 2.83 kB
JavaScript
const nano = require('nano-seconds');
const SECONDS_TO_EPOCH = 62135596800;
const MS_PER_SEC = 1000;
const NS_PER_TICK = 100;
const TICKS_PER_MS = 10000;
const MAX_TS = 253402300799999;
const MIN_TS = -62135596800000;
function toString() {
const us = `${this[1]}`;
if (this[0] === 0) {
return us;
}
const pad = '0000000'.substring(0, 7 - us.length);
return `${this[0]}${pad}${us}`;
}
function toTickObj([secs, ticks]) {
const tarr = [secs, ticks];
Object.defineProperty(tarr, 'toString', {
value: toString
});
return Object.freeze(tarr);
}
function tsToArray(ts) {
const tickTs = ts + (SECONDS_TO_EPOCH * MS_PER_SEC);
return [Math.trunc(tickTs / MS_PER_SEC), ~~(tickTs % MS_PER_SEC) * TICKS_PER_MS];
}
function rollOver(tickArr, overflow) {
tickArr[1] += overflow;
if (tickArr[1] < 0) {
tickArr[1] += (TICKS_PER_MS * MS_PER_SEC);
tickArr[0] -= 1;
}
}
function at(...args) {
if (args.length < 2) {
throw new Error('This function needs to be called with at least two numbers.');
}
let ms = 0;
let overflow = 0;
if (args.length >= 7) {
ms = Math.trunc(args[6] / TICKS_PER_MS);
overflow = args[6] % TICKS_PER_MS;
args[6] = ms;
}
let point = Date.UTC(...args);
if (point < MIN_TS || point > MAX_TS) {
throw new Error('This date is out of range of standard .NET DateTime(Offset)');
}
if (args[0] < 100) {
const pdate = new Date(point);
pdate.setUTCFullYear(args[0]);
point = pdate.getTime();
}
const arr = tsToArray(point);
if (overflow !== 0) {
rollOver(arr, overflow);
}
return toTickObj(arr);
}
function from(dtObj) {
if (Object.prototype.toString.call(dtObj) !== '[object Date]') {
throw new Error('Argument is not a date object');
}
const point = dtObj.getTime();
if (point < MIN_TS || point > MAX_TS) {
throw new Error('This date is out of range of standard .NET DateTime(Offset)');
}
const arr = tsToArray(point);
if (dtObj.getTicks) {
arr[1] += dtObj.getTicks();
}
return toTickObj(arr);
}
function to(ticks) {
let ts, extra;
if (Array.isArray(ticks)) {
ts = ticks[0] * MS_PER_SEC + Math.trunc(ticks[1] / TICKS_PER_MS);
extra = ticks[1] % TICKS_PER_MS;
} else {
if (ticks.length <= 4) {
ts = 0;
} else {
ts = parseInt(ticks.slice(0, -4), 10);
}
extra = parseInt(ticks.slice(-4), 10);
}
const dateObj = new Date(ts - (SECONDS_TO_EPOCH * MS_PER_SEC));
Object.defineProperty(dateObj, 'getTicks', {
value: () => extra || 0
});
return dateObj;
}
function now() {
const [secs, ns] = nano.now();
// ~~ is fast truncation for numbers which don't exceed 32 bits
return toTickObj([secs + SECONDS_TO_EPOCH, ~~(ns / NS_PER_TICK)]);
}
module.exports = {
ticksNow: now,
ticksAt: at,
ticksFromDate: from,
ticksToDate: to
};