@jsenv/humanize 1.7.7 → 1.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -677,10 +677,13 @@ const derivedErrorNameArray = [
|
|
|
677
677
|
];
|
|
678
678
|
|
|
679
679
|
const inspectDate = (value, { nestedHumanize, useNew, parenthesis }) => {
|
|
680
|
-
const dateSource = nestedHumanize(value.
|
|
680
|
+
const dateSource = nestedHumanize(value.toISOString(), {
|
|
681
681
|
numericSeparator: false,
|
|
682
682
|
});
|
|
683
|
-
return inspectConstructor(`Date(${dateSource})`, {
|
|
683
|
+
return inspectConstructor(`Date(${dateSource})`, {
|
|
684
|
+
useNew,
|
|
685
|
+
parenthesis,
|
|
686
|
+
});
|
|
684
687
|
};
|
|
685
688
|
|
|
686
689
|
const inspectFunction = (
|
|
@@ -1014,29 +1017,16 @@ const TIME_DICTIONARY_EN = {
|
|
|
1014
1017
|
second: { long: "second", plural: "seconds", short: "s" },
|
|
1015
1018
|
joinDuration: (primary, remaining) => `${primary} and ${remaining}`,
|
|
1016
1019
|
};
|
|
1017
|
-
const TIME_DICTIONARY_FR = {
|
|
1018
|
-
year: { long: "an", plural: "ans", short: "a" },
|
|
1019
|
-
month: { long: "mois", plural: "mois", short: "m" },
|
|
1020
|
-
week: { long: "semaine", plural: "semaines", short: "s" },
|
|
1021
|
-
day: { long: "jour", plural: "jours", short: "j" },
|
|
1022
|
-
hour: { long: "heure", plural: "heures", short: "h" },
|
|
1023
|
-
minute: { long: "minute", plural: "minutes", short: "m" },
|
|
1024
|
-
second: { long: "seconde", plural: "secondes", short: "s" },
|
|
1025
|
-
joinDuration: (primary, remaining) => `${primary} et ${remaining}`,
|
|
1026
|
-
};
|
|
1027
1020
|
|
|
1028
1021
|
const humanizeEllapsedTime = (
|
|
1029
1022
|
ms,
|
|
1030
|
-
{
|
|
1031
|
-
short,
|
|
1032
|
-
lang = "en",
|
|
1033
|
-
timeDictionnary = lang === "fr" ? TIME_DICTIONARY_FR : TIME_DICTIONARY_EN,
|
|
1034
|
-
} = {},
|
|
1023
|
+
{ short, timeDictionnary = TIME_DICTIONARY_EN } = {},
|
|
1035
1024
|
) => {
|
|
1036
1025
|
if (ms < 1000) {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1026
|
+
if (short) {
|
|
1027
|
+
return `0${timeDictionnary.second.short}`;
|
|
1028
|
+
}
|
|
1029
|
+
return `0 ${timeDictionnary.second.long}`;
|
|
1040
1030
|
}
|
|
1041
1031
|
const { primary, remaining } = parseMs(ms);
|
|
1042
1032
|
if (!remaining) {
|
|
@@ -1068,33 +1058,52 @@ const inspectEllapsedUnit = (unit, { short, timeDictionnary }) => {
|
|
|
1068
1058
|
return `${count} ${unitText}`;
|
|
1069
1059
|
};
|
|
1070
1060
|
|
|
1061
|
+
/**
|
|
1062
|
+
* Converts a duration in milliseconds into a human-readable string intended for display in
|
|
1063
|
+
* CLI output — where readability matters more than precision.
|
|
1064
|
+
*
|
|
1065
|
+
* - Values below 1ms are displayed as "0 second". Sub-millisecond durations are not
|
|
1066
|
+
* meaningful at human scale, and showing "0.0001 second" (or switching to a "millisecond"
|
|
1067
|
+
* unit) would hurt readability. The chosen trade-off is to always use "second" as the
|
|
1068
|
+
* smallest unit and accept the loss of precision for very small values.
|
|
1069
|
+
* - Values below 1s are displayed in fractional seconds (e.g. "0.05 second").
|
|
1070
|
+
* - Values are expressed using the two most significant units (e.g. "1 hour and 23 minutes").
|
|
1071
|
+
* - Rounding never causes a value to display as the next unit boundary
|
|
1072
|
+
* (e.g. 59_999ms → "59.9 seconds", never "60 seconds").
|
|
1073
|
+
*
|
|
1074
|
+
* @param {number} ms - Duration in milliseconds.
|
|
1075
|
+
* @param {object} [options]
|
|
1076
|
+
* @param {boolean} [options.short=false] - Use compact unit symbols (e.g. "1h and 23m").
|
|
1077
|
+
* @param {boolean} [options.rounded=true] - Round the last displayed digit. When false, truncates instead.
|
|
1078
|
+
* @param {number} [options.decimals] - Override the number of decimal places shown.
|
|
1079
|
+
* @returns {string}
|
|
1080
|
+
*/
|
|
1071
1081
|
const humanizeDuration = (
|
|
1072
1082
|
ms,
|
|
1073
1083
|
{
|
|
1074
1084
|
short,
|
|
1075
1085
|
rounded = true,
|
|
1076
1086
|
decimals,
|
|
1077
|
-
|
|
1078
|
-
timeDictionnary = lang === "fr" ? TIME_DICTIONARY_FR : TIME_DICTIONARY_EN,
|
|
1087
|
+
timeDictionnary = TIME_DICTIONARY_EN,
|
|
1079
1088
|
} = {},
|
|
1080
1089
|
) => {
|
|
1081
|
-
// ignore ms below meaningfulMs so that:
|
|
1082
|
-
// humanizeDuration(0.5) -> "0 second"
|
|
1083
|
-
// humanizeDuration(1.1) -> "0.001 second" (and not "0.0011 second")
|
|
1084
|
-
// This tool is meant to be read by humans and it would be barely readable to see
|
|
1085
|
-
// "0.0001 second" (stands for 0.1 millisecond)
|
|
1086
|
-
// yes we could return "0.1 millisecond" but we choosed consistency over precision
|
|
1087
|
-
// so that the prefered unit is "second" (and does not become millisecond when ms is super small)
|
|
1088
1090
|
if (ms < 1) {
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1091
|
+
if (short) {
|
|
1092
|
+
return `0${timeDictionnary.second.short}`;
|
|
1093
|
+
}
|
|
1094
|
+
return `0 ${timeDictionnary.second.long}`;
|
|
1092
1095
|
}
|
|
1093
1096
|
const { primary, remaining } = parseMs(ms);
|
|
1094
1097
|
if (!remaining) {
|
|
1098
|
+
const primaryUnitIndex = UNIT_KEYS.indexOf(primary.name);
|
|
1099
|
+
const nextUnitName = UNIT_KEYS[primaryUnitIndex - 1];
|
|
1100
|
+
const maxCount = nextUnitName
|
|
1101
|
+
? UNIT_MS[nextUnitName] / UNIT_MS[primary.name]
|
|
1102
|
+
: null;
|
|
1095
1103
|
return humanizeDurationUnit(primary, {
|
|
1096
1104
|
decimals:
|
|
1097
1105
|
decimals === undefined ? (primary.name === "second" ? 1 : 0) : decimals,
|
|
1106
|
+
maxCount,
|
|
1098
1107
|
short,
|
|
1099
1108
|
rounded,
|
|
1100
1109
|
timeDictionnary,
|
|
@@ -1112,15 +1121,23 @@ const humanizeDuration = (
|
|
|
1112
1121
|
rounded,
|
|
1113
1122
|
timeDictionnary,
|
|
1114
1123
|
});
|
|
1124
|
+
if (short) {
|
|
1125
|
+
return `${primaryText}${remainingText}`;
|
|
1126
|
+
}
|
|
1115
1127
|
return timeDictionnary.joinDuration(primaryText, remainingText);
|
|
1116
1128
|
};
|
|
1117
1129
|
const humanizeDurationUnit = (
|
|
1118
1130
|
unit,
|
|
1119
|
-
{ decimals, short, rounded, timeDictionnary },
|
|
1131
|
+
{ decimals, maxCount, short, rounded, timeDictionnary },
|
|
1120
1132
|
) => {
|
|
1121
|
-
|
|
1133
|
+
let count = rounded
|
|
1122
1134
|
? setRoundedPrecision(unit.count, { decimals })
|
|
1123
1135
|
: setPrecision(unit.count, { decimals });
|
|
1136
|
+
if (maxCount !== null && maxCount !== undefined && count >= maxCount) {
|
|
1137
|
+
// Prevent rounding up to the next unit boundary (e.g. 59.999s → 60s → cap to 59.9s)
|
|
1138
|
+
const factor = Math.pow(10, decimals ?? 0);
|
|
1139
|
+
count = Math.floor(unit.count * factor) / factor;
|
|
1140
|
+
}
|
|
1124
1141
|
const name = unit.name;
|
|
1125
1142
|
if (short) {
|
|
1126
1143
|
const unitText = timeDictionnary[name].short;
|
|
@@ -1173,6 +1190,17 @@ const parseMs = (ms) => {
|
|
|
1173
1190
|
},
|
|
1174
1191
|
};
|
|
1175
1192
|
}
|
|
1193
|
+
// When remaining rounds up to a full next-unit (e.g. 59.999s rounds to 60s = 1min),
|
|
1194
|
+
// drop the remaining to avoid displaying "59 minutes and 60 seconds".
|
|
1195
|
+
const remainingUnitMs = UNIT_MS[remainingUnitName];
|
|
1196
|
+
const nextUnitMs = UNIT_MS[firstUnitName];
|
|
1197
|
+
const maxRemainingCount = nextUnitMs / remainingUnitMs; // e.g. 60 for seconds-in-a-minute
|
|
1198
|
+
// Cap remaining so it never rounds up to the next unit boundary
|
|
1199
|
+
// (e.g. 59.5s stays as 59s instead of rounding to 60s = 1min)
|
|
1200
|
+
const cappedRemainingCount =
|
|
1201
|
+
remainingUnitCount >= maxRemainingCount - 1
|
|
1202
|
+
? maxRemainingCount - 1
|
|
1203
|
+
: remainingUnitCount;
|
|
1176
1204
|
// - 1 year and 1 month is great
|
|
1177
1205
|
return {
|
|
1178
1206
|
primary: {
|
|
@@ -1181,7 +1209,7 @@ const parseMs = (ms) => {
|
|
|
1181
1209
|
},
|
|
1182
1210
|
remaining: {
|
|
1183
1211
|
name: remainingUnitName,
|
|
1184
|
-
count:
|
|
1212
|
+
count: cappedRemainingCount,
|
|
1185
1213
|
},
|
|
1186
1214
|
};
|
|
1187
1215
|
};
|