blob: 0fc6bd7b9fa97bb03057b034c30736d7f3e7ea8f [file] [log] [blame]
/** @module common-sk/modules/human
* @description Utitities for working with human friendly I/O.
*/
const TIME_DELTAS = [
{ units: "w", delta: 7*24*60*60 },
{ units: "d", delta: 24*60*60 },
{ units: "h", delta: 60*60 },
{ units: "m", delta: 60 },
{ units: "s", delta: 1 },
];
/** @constant {number} */
export const KB = 1024;
/** @constant {number} */
export const MB = KB * 1024;
/** @constant {number} */
export const GB = MB * 1024;
/** @constant {number} */
export const TB = GB * 1024;
/** @constant {number} */
export const PB = TB * 1024;
const BYTES_DELTAS = [
{ units: " PB", delta: PB},
{ units: " TB", delta: TB},
{ units: " GB", delta: GB},
{ units: " MB", delta: MB},
{ units: " KB", delta: KB},
{ units: " B", delta: 1},
];
/** Left pad a number with 0's.
*
* @param {number} num - The number to pad.
* @param {number} size - The number of digits to pad out to.
* @returns {string}
*/
export function pad(num, size) {
let str = num + "";
while (str.length < size) str = "0" + str;
return str;
}
/**
* Returns a human-readable format of the given duration in seconds.
* For example, 'strDuration(123)' would return "2m 3s".
* Negative seconds is treated the same as positive seconds.
*
* @param {number} seconds - The duration.
* @returns {string}
*/
export function strDuration(seconds) {
if (seconds < 0) {
seconds = -seconds;
}
if (seconds === 0) { return ' 0s'; }
let rv = "";
for (let i=0; i<TIME_DELTAS.length; i++) {
if (TIME_DELTAS[i].delta <= seconds) {
let s = Math.floor(seconds/TIME_DELTAS[i].delta)+TIME_DELTAS[i].units;
while (s.length < 4) {
s = ' ' + s;
}
rv += s;
seconds = seconds % TIME_DELTAS[i].delta;
}
}
return rv;
};
/**
* Returns the difference between the current time and 's' as a string in a
* human friendly format. If 's' is a number it is assumed to contain the time
* in milliseconds otherwise it is assumed to contain a time string parsable
* by Date.parse().
*
* For example, a difference of 123 seconds between 's' and the current time
* would return "2m".
*
* @param {Object} milliseconds - The time in milliseconds or a time string.
* @returns {string}
*/
export function diffDate(s) {
let ms = (typeof(s) === "number") ? s : Date.parse(s);
let diff = (ms - Date.now())/1000;
if (diff < 0) {
diff = -1.0 * diff;
}
return humanize(diff, TIME_DELTAS);
}
/**
* Formats the amount of bytes in a human friendly format.
* unit may be supplied to indicate b is not in bytes, but in something
* like kilobytes (KB) or megabytes (MB)
*
* @example
* // returns "1 KB"
* bytes(1234)
* @example
* // returns "5 GB"
* bytes(5321, MB)
*
* @param {number} b - The number of bytes in units 'unit'.
* @param {number} unit - The number of bytes per unit.
* @returns {string}
*/
export function bytes(b, unit = 1) {
if (Number.isInteger(unit)) {
b = b * unit;
}
return humanize(b, BYTES_DELTAS);
}
/** localeTime formats the provided Date object in locale time and appends the timezone to the end.
*
* @param {Date} date
* @returns {string}
*/
export function localeTime(date) {
// caching timezone could be buggy, especially if times from a wide range
// of dates are used. The main concern would be crossing over Daylight
// Savings time and having some times be erroneously in EST instead of
// EDT, for example
let str = date.toString();
let timezone = str.substring(str.indexOf("("));
return date.toLocaleString() + " " + timezone;
}
function humanize(n, deltas) {
for (let i=0; i<deltas.length-1; i++) {
// If n would round to '60s', return '1m' instead.
let nextDeltaRounded =
Math.round(n/deltas[i+1].delta)*deltas[i+1].delta;
if (nextDeltaRounded/deltas[i].delta >= 1) {
return Math.round(n/deltas[i].delta)+deltas[i].units;
}
}
let i = deltas.length-1;
return Math.round(n/deltas[i].delta)+deltas[i].units;
}