blob: 600204a7ed99534f4148de8f11b83762f3e079f0 [file] [log] [blame]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/** @module common-sk/modules/human
* @description Utitities for working with human friendly I/O.
*/
interface Delta {
readonly units: string;
readonly delta: number;
}
const TIME_DELTAS: Delta[] = [
{ 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: Delta[] = [
{ 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 0s.
*
* @param num - The number to pad.
* @param size - The number of digits to pad out to.
*/
export function pad(num: number, size: number): string {
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 seconds - The duration.
*/
export function strDuration(seconds: number): string {
if (seconds < 0) {
seconds = -seconds;
}
if (seconds === 0) {
return ' 0s';
}
let rv = '';
for (const td of TIME_DELTAS) {
if (td.delta <= seconds) {
let s = Math.floor(seconds / td.delta) + td.units;
while (s.length < 4) {
s = ` ${s}`;
}
rv += s;
seconds %= td.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 milliseconds - The time in milliseconds or a time string.
* @param now - The time to diff against, if not supplied then the diff
* is done against Date.now().
*/
export function diffDate(s: number | string, now?: number): string {
if (now === undefined) {
now = Date.now();
}
const ms = typeof s === 'number' ? s : Date.parse(s);
let diff = (ms - now) / 1000;
if (diff < 0) {
// eslint-disable-next-line operator-assignment
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 b - The number of bytes in units 'unit'.
* @param unit - The number of bytes per unit.
*/
export function bytes(b: number, unit: number = 1): string {
return humanize(b * unit, BYTES_DELTAS);
}
/** localeTime formats the provided Date object in locale time and appends the timezone to the end.
*
* @param date The date to format.
*/
export function localeTime(date: Date): string {
// 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
const str = date.toString();
const timezone = str.substring(str.indexOf('('));
return `${date.toLocaleString()} ${timezone}`;
}
function humanize(n: number, deltas: Delta[]) {
for (let i = 0; i < deltas.length - 1; i++) {
// If n would round to '60s', return '1m' instead.
const 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;
}
}
const index = deltas.length - 1;
return Math.round(n / deltas[index].delta) + deltas[index].units;
}