timespan-parser
Version:
Parses timespans like systemd.time(7)
175 lines (161 loc) • 5.33 kB
JavaScript
;
const assert = require("assert");
const ms = 1;
const s = 1000 * ms;
const m = 60 * s;
const h = 60 * m;
const d = 24 * h;
const w = 7 * d;
const M = 30.44 * d;
const y = 365.25 * d;
const UNITS = {
msec: ms,
ms,
seconds: s,
second: s,
sec: s,
"": s,
s,
minutes: m,
minute: m,
min: m,
m,
hours: h,
hour: h,
hr: h,
h,
days: d,
day: d,
d,
weeks: w,
week: w,
w,
months: M,
month: M,
M,
years: y,
year: y,
y
};
const DEFAULTS = {
abbv: true,
capitalize: false,
valueSep: " "
};
const longname = {
ms: "millisecond",
s: "second",
m: "minute",
h: "hour",
d: "day",
w: "week",
M: "month",
y: "year"
};
function init(initOpts = {}) {
const units = { ...UNITS };
if (typeof initOpts == "string") {
initOpts = { unit: initOpts };
}
assert.ok(initOpts != null && typeof initOpts == "object", `Invalid defaults, expected object but got ${typeof initOpts}`);
const defaults = {
...DEFAULTS,
...initOpts
};
if (defaults.unit) {
assert.strictEqual(typeof defaults.unit, "string", `Invalid unit, expected string but got ${typeof defaults.unit}`);
const val = units[defaults.unit];
assert.ok(val, `Unknown unit ${defaults.unit}`);
units[""] = val;
}
if (defaults.unitSep != null) {
assert.strictEqual(typeof defaults.unitSep, "string", `Invalid unitSep, expected string but got ${typeof defaults.unitSep}`);
}
assert.strictEqual(typeof defaults.valueSep, "string", `Invalid valueSep, expected string but got ${typeof defaults.valueSep}`);
return {
parse,
getString
};
function parse(timespan, unit) {
assert.ok(timespan, "Expected timespan");
if (typeof timespan === "number") {
timespan += "";
}
assert.strictEqual(typeof timespan, "string", `Invalid timespan, expected string but got ${typeof timespan}`);
if (unit == null) {
unit = "";
}
assert.strictEqual(typeof unit, "string", `Invalid unit, expected string but got ${typeof unit}`);
const divider = units[unit];
assert.ok(divider, `Unknown unit ${unit}`);
const tss = /^\s*-?(\d+\s*[a-z]*\s*)+$/.test(timespan) && timespan.match(/\d+\s*[a-z]*\s*/g);
assert.ok(tss, `Invalid format for timespan ${timespan}`);
const sign = timespan.trim().startsWith("-") ? -1 : 1;
const value = tss.reduce((sum, ts) => sum + getValue(ts), 0);
return sign * (value / divider);
}
function getValue(ts) {
const n = parseInt(ts, 10);
const suf = ts.replace(/[\d\s-]/g, "");
const mutliplier = units[suf];
assert.ok(mutliplier, `Invalid timespan, unknown unit ${suf}`);
return n * mutliplier;
}
function getString(value, opts = {}) {
if (typeof opts === "string") {
opts = { unit: opts };
}
if (opts == null) {
opts = {};
}
const {
unit = "",
abbv = defaults.abbv,
capitalize = defaults.capitalize,
unitSep = defaults.unitSep != null ? defaults.unitSep : abbv ? "" : " ",
valueSep = defaults.valueSep
} = opts;
if (typeof value === "string" && /^-?\d+(\.\d+)?$/.test(value)) {
value = +value;
}
assert.strictEqual(typeof value, "number", `Invalid value, expected number but got ${typeof value}`);
assert.ok(isFinite(value), `Invalid value ${value}`);
assert.strictEqual(typeof unit, "string", `Invalid unit, expected string but got ${typeof unit}`);
assert.ok(units[unit], `Unknown unit ${unit}`);
assert.strictEqual(typeof abbv, "boolean", `Invalid abbv, expected boolean but got ${typeof abbv}`);
assert.strictEqual(typeof capitalize, "boolean", `Invalid capitalize, expected boolean but got ${typeof capitalize}`);
assert.strictEqual(typeof unitSep, "string", `Invalid unitSep, expected string but got ${typeof unitSep}`);
assert.strictEqual(typeof valueSep, "string", `Invalid valueSep, expected string but got ${typeof valueSep}`);
const tss = [];
const sign = value < 0 ? "-" : "";
value = Math.abs(Math.round(value * units[unit]));
const writeValue = unitName => {
const unitVal = units[unitName];
if (value >= unitVal) {
tss.push(format(Math.floor(value / unitVal), unitName));
value %= unitVal;
}
};
writeValue("y");
writeValue("w");
writeValue("d");
writeValue("h");
writeValue("m");
writeValue("s");
writeValue("ms");
return sign + tss.join(valueSep);
function format(val, unitName) {
if (!abbv) {
unitName = longname[unitName];
if (val != 1) {
unitName += "s";
}
}
if (capitalize) {
unitName = unitName[0].toUpperCase() + unitName.slice(1);
}
return val + unitSep + unitName;
}
}
}
module.exports = Object.assign(init, init());