module.exports = NanoTime;
/**
* @private
*/
const ZERO = BigInt(0);
/**
* @private
*/
const THOUSAND = BigInt(1000); // eslint-disable-line no-magic-numbers
/**
* @private
*/
const NANOSECOND = BigInt(1);
/**
* @private
*/
const MICROSECOND = NANOSECOND * THOUSAND;
/**
* @private
*/
const MILLISECOND = MICROSECOND * THOUSAND;
/**
* @private
*/
const SECOND = MILLISECOND * THOUSAND;
/**
* @private
*/
const MINUTES_IN_SECONDS = 60;
/**
* @example
* const NanoTime = require('pete/lib/nanotime');
* var time = new NanoTime();
*
* @alias module:pete/lib/NanoTime
* @class
* @param {number|bigint} [value=0n]
*/
function NanoTime (value = ZERO) {
this.value = value;
this.seconds = 0;
this.milliseconds = 0;
this.microseconds = 0;
this.nanoseconds = 0;
this.set(value);
}
/**
* Value of nanosecond.
*/
NanoTime.NANOSECOND = NANOSECOND;
/**
* Value of one microsecond in nanoseconds.
*/
NanoTime.MICROSECOND = MICROSECOND;
/**
* Value of one millisecond in nanoseconds.
*/
NanoTime.MILLISECOND = MILLISECOND;
/**
* Value of one second in nanoseconds.
*/
NanoTime.SECOND = SECOND;
/**
* Create NanoTime from value.
* If called on existing NanoTime object, it will update its value instead of creating new object.
* If value type is not supported, simply return that value.
*
* @static
* @param {number|bigint|string|module:lib/nanotime~NanoTime} value
* @return {module:lib/nanotime~NanoTime|any}
*/
NanoTime.from = function from (value) {
if (this instanceof NanoTime) {
this.value = ZERO;
}
if (typeof value === 'number') {
return fromNumber.call(this, value);
}
else if (typeof value === 'bigint') {
return fromBigInt.call(this, value);
}
else if (typeof value === 'string') {
return fromString.call(this, value);
}
else if (typeof value === 'object') {
return fromNanoTime.call(this, value);
}
return value;
};
/**
* Set new value.
*
* @method
* @see {@link NanoTime.from}
* @param {string|number|bigint|module:lib/nanotime~NanoTime} value
* @return {module:lib/nanotime~NanoTime|any}
*/
NanoTime.prototype.set = NanoTime.from;
/**
* Get number of nanoseconds.
*
* @example
* var time = new NanoTime(process.hrtime.bigint());
* console.log(time.get()); // 1002003004n
*
* @return {bigint}
*/
NanoTime.prototype.get = function get () {
return this.value;
};
/**
* Get number of nanoseconds.
*
* @method
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf}
* @return {bigint}
*/
NanoTime.prototype.valueOf = NanoTime.prototype.get;
/**
* Convert to primitive type.
*
* @private
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toPrimitive}
* @param {string} hint
* @return {bigint|number|string|boolean}
*/
NanoTime.prototype[Symbol.toPrimitive] = function toPrimitive (hint) {
if (hint === 'bigint') {
return this.value;
}
if (hint === 'number') {
return this.toNumber();
}
if (hint === 'string') {
return this.toString();
}
return this.value !== ZERO;
};
/**
* Convert to textual representation of value.
*
* @example
* var time = new NanoTime(process.hrtime.bigint());
* console.log(time); // 1m 2s 3ms 4μs 5ns
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString}
* @return {string}
*/
NanoTime.prototype.toString = function toString () {
let seconds = this.seconds;
const minutes = seconds > MINUTES_IN_SECONDS ? Math.floor(seconds / MINUTES_IN_SECONDS) : 0;
if (minutes > 0) {
seconds -= minutes * MINUTES_IN_SECONDS;
}
return `${minutes}m ${seconds}s ${this.milliseconds}ms ${this.microseconds}μs ${this.nanoseconds}ns`;
};
/**
* Convert to Number.
*
* @example
* var time = new NanoTime(process.hrtime.bigint());
* console.log(time.toNumber()); // 1.002003004
*
* @return {number}
*/
NanoTime.prototype.toNumber = function toNumber () {
/* eslint-disable */
return Number(this.seconds + '.' +
('000' + this.milliseconds).slice(-3) +
('000' + this.microseconds).slice(-3) +
('000' + this.nanoseconds).slice(-3)
);
/* eslint-enable */
};
/**
* When stringified to JSON, output String representation instead of full object.
*
* @example
* var time = new NanoTime(process.hrtime.bigint());
* console.log(JSON.stringify(time)); // "1m 2s 3ms 4μs 5ns"
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior}
* @return {string}
*/
NanoTime.prototype.toJSON = function toJSON () {
return this.toString();
};
/**
* When stringified for inspection, output String representation instead of full object.
*
* @private
* @see {@link https://nodejs.org/api/util.html#util_util_inspect_custom}
* @return {string}
*/
NanoTime.prototype[Symbol.for('nodejs.util.inspect.custom')] = function inspect () {
return `'${this.toString()}'`;
};
/* eslint-disable no-invalid-this */
/**
* If called on existing NanoTime, update its value. Create new NanoTime otherwise.
* If value type is not supported, just return that value.
*
* @private
* @param {bigint} value
* @return {module:lib/nanotime~NanoTime|any}
*/
function fromBigInt (value) {
if (typeof value !== 'bigint') {
return value;
}
if (!this || !(this instanceof NanoTime)) {
return new NanoTime(value);
}
this.value = value;
var n = 0;
var b = ZERO;
var sum = ZERO;
b = value / SECOND;
sum += b * SECOND;
n = Number(b);
if (n >= 1) {
this.seconds = Math.floor(n);
}
b = (value - sum) / MILLISECOND;
sum += b * MILLISECOND;
n = Number(b);
if (n >= 1) {
this.milliseconds = Math.floor(n);
}
b = (value - sum) / MICROSECOND;
sum += b * MICROSECOND;
n = Number(b);
if (n >= 1) {
this.microseconds = Math.floor(n);
}
n = Number(value - sum);
if (n >= 1) {
this.nanoseconds = Math.floor(n);
}
return this;
}
/**
* If called on existing NanoTime, update its value. Create new NanoTime otherwise.
* If value type is not supported, just return that value.
*
* @private
* @param {number} value
* @return {module:lib/nanotime~NanoTime|any}
*/
function fromNumber (value) {
if (typeof value !== 'number' || isNaN(value) || value === Infinity) {
return value;
}
if (!this || !(this instanceof NanoTime)) {
return new NanoTime(value);
}
this.seconds = Math.floor(value);
// 9 = 3 per value * 3 values (mili-, micro- and nanoseconds)
var n = value.toFixed(9).split('.')[1].match(/.{1,3}/g); // eslint-disable-line no-magic-numbers
/* eslint-disable array-element-newline */
[
this.milliseconds = 0,
this.microseconds = 0,
this.nanoseconds = 0
] = n.map(Number);
/* eslint-enable array-element-newline */
this.value = ZERO
+ (BigInt(this.seconds) * SECOND)
+ (BigInt(this.milliseconds) * MILLISECOND)
+ (BigInt(this.microseconds) * MICROSECOND)
+ BigInt(this.nanoseconds);
return this;
}
/**
* If called on existing NanoTime, update its value. Create new NanoTime otherwise.
* If value type is not supported, just return that value.
*
* @private
* @param {string} value
* @return {module:lib/nanotime~NanoTime|any}
*/
function fromString (value) {
if (typeof value !== 'string') {
return value;
}
var n = value.match(/^(?:\s*)(?:(?<m>\d+)m ?)?(?:(?<s>\d+)s)?(?: (?<ms>\d+)ms)?(?: (?<us>\d+)(?:μ|u)s)?(?: (?<ns>\d+)ns)?(?:\s*)$/);
if (!n) {
return value;
}
if (!this || !(this instanceof NanoTime)) {
return new NanoTime(value);
}
this.seconds = Number(n.groups.s || 0) + (Number(n.groups.m || 0) * MINUTES_IN_SECONDS);
this.milliseconds = Number(n.groups.ms || 0);
this.microseconds = Number(n.groups.us || 0);
this.nanoseconds = Number(n.groups.ns || 0);
this.value = ZERO
+ (BigInt(this.seconds) * SECOND)
+ (BigInt(this.milliseconds) * MILLISECOND)
+ (BigInt(this.microseconds) * MICROSECOND)
+ BigInt(this.nanoseconds);
return this;
}
/**
* If called on existing NanoTime, update its value. Create new NanoTime otherwise.
* If value type is not supported, just return that value.
*
* @private
* @param {module:lib/nanotime~NanoTime} value
* @return {module:lib/nanotime~NanoTime|any}
*/
function fromNanoTime (value) {
if (typeof value !== 'object') {
return value;
}
if (typeof value.value !== 'bigint') {
return value;
}
if (!(value instanceof NanoTime)) {
return fromBigInt.call(this, value.value);
}
if (!this || !(this instanceof NanoTime)) {
return new NanoTime(value);
}
this.value = value.value;
this.nanoseconds = value.nanoseconds;
this.microseconds = value.microseconds;
this.milliseconds = value.milliseconds;
this.seconds = value.seconds;
return this;
}