@storm-stack/date-time
Version:
This package includes a DateTime class, various utility functions for working with dates and times, and a number of formatting options.
134 lines (133 loc) • 5.55 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.formatSince = void 0;
var _errors = require("@storm-stack/errors");
var _types = require("@storm-stack/types");
var _errors2 = require("../errors.cjs");
var _stormDateTime = require("../storm-date-time.cjs");
var _isDateTime = require("./is-date-time.cjs");
const pluralize = (word, count) => count === 1 ? word : `${word}s`;
const SECOND_ROUNDING_EPSILON = 1e-7;
const parseMilliseconds = milliseconds => {
if (!Number.isFinite(milliseconds)) {
throw _errors.StormError.createValidation({
code: _errors2.DateTimeErrorCode.ms_format,
type: _types.MessageType.ERROR
}, {
message: "Method `parseMilliseconds` expected a finite number"
});
}
return {
days: Math.trunc(milliseconds / 864e5),
hours: Math.trunc(milliseconds / 36e5) % 24,
minutes: Math.trunc(milliseconds / 6e4) % 60,
seconds: Math.trunc(milliseconds / 1e3) % 60,
milliseconds: Math.trunc(milliseconds) % 1e3,
microseconds: Math.trunc(milliseconds * 1e3) % 1e3,
nanoseconds: Math.trunc(milliseconds * 1e6) % 1e3
};
};
const formatSince = (dateTimeOrDuration, dateTimeTo = _stormDateTime.StormDateTime.current(), options) => {
const colonNotation = options?.colonNotation ?? false;
let compact = options?.compact ?? false;
let formatSubMilliseconds = options?.formatSubMilliseconds ?? false;
const keepDecimalsOnWholeSeconds = options?.keepDecimalsOnWholeSeconds ?? false;
let millisecondsDecimalDigits = options?.millisecondsDecimalDigits ?? 0;
let secondsDecimalDigits = options?.secondsDecimalDigits ?? 1;
let separateMilliseconds = options?.separateMilliseconds ?? false;
const unitCount = options?.unitCount ?? 0;
let verbose = options?.verbose ?? false;
let milliseconds;
milliseconds = (0, _isDateTime.isDateTime)(dateTimeOrDuration) ? dateTimeTo.since(dateTimeOrDuration).milliseconds : dateTimeOrDuration.milliseconds;
if (!Number.isFinite(milliseconds)) {
throw _errors.StormError.createValidation({
code: _errors2.DateTimeErrorCode.ms_format,
type: _types.MessageType.ERROR
}, {
message: "Method `formatSince` expected a finite number"
});
}
if (milliseconds < 0) {
milliseconds *= -1;
}
if (colonNotation) {
compact = false;
formatSubMilliseconds = false;
separateMilliseconds = false;
verbose = false;
}
if (compact) {
secondsDecimalDigits = 0;
millisecondsDecimalDigits = 0;
}
const result = [];
const floorDecimals = (value, decimalDigits) => {
const flooredInterimValue = Math.floor(value * 10 ** decimalDigits + SECOND_ROUNDING_EPSILON);
const flooredValue = Math.round(flooredInterimValue) / 10 ** decimalDigits;
return flooredValue.toFixed(decimalDigits);
};
const add = (value, long, short, valueString) => {
if ((result.length === 0 || !colonNotation) && value === 0 && !(colonNotation && short === "m")) {
return;
}
let _valueString = (valueString || value || "0").toString();
let prefix;
let suffix;
if (colonNotation) {
prefix = result.length > 0 ? ":" : "";
suffix = "";
const wholeDigits = _valueString.includes(".") ? _valueString.split(".")[0]?.length ?? 0 : _valueString.length;
const minLength = result.length > 0 ? 2 : 1;
_valueString = "0".repeat(Math.max(0, minLength - wholeDigits)) + _valueString;
} else {
prefix = "";
suffix = verbose ? ` ${pluralize(long, value)}` : short;
}
result.push(prefix + _valueString + suffix);
};
const parsed = parseMilliseconds(milliseconds);
add(Math.trunc(parsed.days / 365), "year", "y");
add(parsed.days % 365, "day", "d");
add(parsed.hours, "hour", "h");
add(parsed.minutes, "minute", "m");
if (separateMilliseconds || formatSubMilliseconds || !colonNotation && milliseconds < 1e3) {
add(parsed.seconds, "second", "s");
if (formatSubMilliseconds) {
add(parsed.milliseconds, "millisecond", "ms");
add(parsed.microseconds, "microsecond", "\xB5s");
add(parsed.nanoseconds, "nanosecond", "ns");
} else {
const millisecondsAndBelow = parsed.milliseconds + parsed.microseconds / 1e3 + parsed.nanoseconds / 1e6;
const roundedMilliseconds = millisecondsAndBelow >= 1 ? Math.round(millisecondsAndBelow) : Math.ceil(millisecondsAndBelow);
const millisecondsString = millisecondsDecimalDigits ? millisecondsAndBelow.toFixed(millisecondsDecimalDigits) : String(roundedMilliseconds);
add(Number.parseFloat(millisecondsString), "millisecond", "ms", millisecondsString);
}
} else {
const seconds = milliseconds / 1e3 % 60;
const secondsFixed = floorDecimals(seconds, secondsDecimalDigits);
const secondsString = keepDecimalsOnWholeSeconds ? secondsFixed : secondsFixed.replace(/\.0+$/, "");
add(Number.parseFloat(secondsString), "second", "s", secondsString);
}
if (result.length === 0) {
return `0${verbose ? " milliseconds" : "ms"}`;
}
if (compact) {
if (!result[0]) {
throw _errors.StormError.createValidation({
code: _errors2.DateTimeErrorCode.formatting_failure,
type: _types.MessageType.ERROR
}, {
message: "Unexpected empty result"
});
}
return result[0];
}
if (typeof unitCount === "number") {
const separator = colonNotation ? "" : " ";
return result.slice(0, Math.max(unitCount, 1)).join(separator);
}
return colonNotation ? result.join("") : result.join(" ");
};
exports.formatSince = formatSince;