Format Duration
Format duration in milliseconds/seconds to human readable (e.g., '2h 30m', '5 days')
Source Code
import fs from "fs";
import path from "path";
const [inputPath, durationField = "", unit = "ms", outputField = "duration", outputPath = ""] = process.argv.slice(2);
if (!inputPath) {
console.error("Usage: stdlib.time.duration <inputPath|duration> [durationField] [unit] [outputField] [outputPath]");
process.exit(1);
}
/**
* Format duration to human readable string
*/
function formatDuration(durationMs, compact = false) {
if (typeof durationMs !== "number" || isNaN(durationMs)) return "unknown";
if (durationMs < 0) durationMs = Math.abs(durationMs);
const seconds = Math.floor(durationMs / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const weeks = Math.floor(days / 7);
const months = Math.floor(days / 30);
const years = Math.floor(days / 365);
if (compact) {
// Short format: "2h 30m"
if (years > 0) return `${years}y ${days % 365}d`;
if (months > 0) return `${months}mo ${days % 30}d`;
if (weeks > 0) return `${weeks}w ${days % 7}d`;
if (days > 0) return `${days}d ${hours % 24}h`;
if (hours > 0) return `${hours}h ${minutes % 60}m`;
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
if (seconds > 0) return `${seconds}s`;
return `${durationMs}ms`;
}
// Long format: "2 hours 30 minutes"
if (years > 0) return `${years} year${years !== 1 ? "s" : ""} ${days % 365} day${days % 365 !== 1 ? "s" : ""}`;
if (months > 0) return `${months} month${months !== 1 ? "s" : ""} ${days % 30} day${days % 30 !== 1 ? "s" : ""}`;
if (weeks > 0) return `${weeks} week${weeks !== 1 ? "s" : ""} ${days % 7} day${days % 7 !== 1 ? "s" : ""}`;
if (days > 0) return `${days} day${days !== 1 ? "s" : ""} ${hours % 24} hour${hours % 24 !== 1 ? "s" : ""}`;
if (hours > 0) return `${hours} hour${hours !== 1 ? "s" : ""} ${minutes % 60} minute${minutes % 60 !== 1 ? "s" : ""}`;
if (minutes > 0) return `${minutes} minute${minutes !== 1 ? "s" : ""} ${seconds % 60} second${seconds % 60 !== 1 ? "s" : ""}`;
if (seconds > 0) return `${seconds} second${seconds !== 1 ? "s" : ""}`;
return `${durationMs} millisecond${durationMs !== 1 ? "s" : ""}`;
}
/**
* Get nested field value
*/
function getField(obj, fieldPath) {
if (!fieldPath) return obj;
const parts = fieldPath.split(".");
let value = obj;
for (const part of parts) {
if (value == null) return undefined;
value = value[part];
}
return value;
}
/**
* Set nested field value
*/
function setField(obj, fieldPath, value) {
const parts = fieldPath.split(".");
let current = obj;
for (let i = 0; i < parts.length - 1; i++) {
if (!current[parts[i]]) current[parts[i]] = {};
current = current[parts[i]];
}
current[parts[parts.length - 1]] = value;
}
/**
* Convert to milliseconds
*/
function toMs(value, unit) {
const num = typeof value === "number" ? value : parseFloat(value);
if (isNaN(num)) return NaN;
return unit === "s" ? num * 1000 : num;
}
try {
let result;
if (fs.existsSync(inputPath)) {
const raw = fs.readFileSync(inputPath, "utf-8");
const data = JSON.parse(raw);
if (Array.isArray(data)) {
result = data.map(item => {
const durationValue = getField(item, durationField);
const durationMs = toMs(durationValue, unit);
const newItem = { ...item };
setField(newItem, outputField, formatDuration(durationMs, true));
setField(newItem, `${outputField}Long`, formatDuration(durationMs, false));
return newItem;
});
console.log(`Formatted ${result.length} durations`);
} else if (typeof data === "number") {
const durationMs = toMs(data, unit);
result = {
raw: data,
[outputField]: formatDuration(durationMs, true),
[`${outputField}Long`]: formatDuration(durationMs, false)
};
console.log(`Formatted: ${result[outputField]}`);
} else {
const durationValue = getField(data, durationField);
const durationMs = toMs(durationValue, unit);
result = { ...data };
setField(result, outputField, formatDuration(durationMs, true));
setField(result, `${outputField}Long`, formatDuration(durationMs, false));
console.log(`Formatted: ${result[outputField]}`);
}
} else {
// Treat input as a duration value
const durationMs = toMs(inputPath, unit);
result = {
raw: inputPath,
[outputField]: formatDuration(durationMs, true),
[`${outputField}Long`]: formatDuration(durationMs, false)
};
console.log(`Formatted: ${result[outputField]}`);
}
if (outputPath) {
const dir = path.dirname(outputPath);
if (dir && dir !== ".") fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(outputPath, JSON.stringify(result, null, 2));
console.log(`Written to: ${outputPath}`);
}
console.log(JSON.stringify({ success: true, result: Array.isArray(result) ? { count: result.length } : result }));
} catch (error) {
console.error("Error:", error.message);
process.exit(1);
}