mirror of
https://github.com/garethgeorge/backrest.git
synced 2026-05-04 03:50:30 +00:00
chore: optimize formatDuration
This commit is contained in:
+24
-30
@@ -60,49 +60,43 @@ export const formatDate = (time: number | string | Date) => {
|
||||
return isoStr.substring(0, 10);
|
||||
};
|
||||
|
||||
const durationUnits = ["seconds", "minutes", "hours", "days"] as const;
|
||||
type DurationUnit = typeof durationUnits[number];
|
||||
const durationSteps = [1000, 60, 60, 24, Number.MAX_VALUE];
|
||||
const durationFactors = [1, 1000, 60 * 1000, 60 * 60 * 1000, 24 * 60 * 60 * 1000];
|
||||
const shortDurationUnits = ["ms", "s", "m", "h", "d"];
|
||||
type DurationUnit = typeof shortDurationUnits[number];
|
||||
|
||||
export interface FormatDurationOptions {
|
||||
minUnit?: DurationUnit;
|
||||
maxUnit?: DurationUnit;
|
||||
}
|
||||
|
||||
|
||||
export const formatDuration = (ms: number, options?: FormatDurationOptions) => {
|
||||
const minUnitIndex = durationUnits.indexOf(options?.minUnit || "seconds");
|
||||
const maxUnitIndex = durationUnits.indexOf(options?.maxUnit || "hours");
|
||||
if (!ms && ms !== 0) return "";
|
||||
|
||||
const seconds = Math.ceil(ms / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
if (!options && ms < 60 * 1000) {
|
||||
// If no options and less than a minute, show seconds
|
||||
// Performance optimization
|
||||
return `${Math.round(ms / 1000)}s`;
|
||||
}
|
||||
|
||||
let parts: string[] = [];
|
||||
const minUnitIndex = options?.minUnit ? shortDurationUnits.indexOf(options.minUnit) : 1; // Don't show ms by default
|
||||
const maxUnitIndex = options?.maxUnit ? shortDurationUnits.indexOf(options.maxUnit) : shortDurationUnits.length - 1;
|
||||
|
||||
if (maxUnitIndex >= 3 && days > 0) {
|
||||
parts.push(`${days}d`);
|
||||
}
|
||||
if (maxUnitIndex >= 2 && minUnitIndex <= 2) {
|
||||
const h = maxUnitIndex === 2 ? hours : (hours % 24);
|
||||
if (h > 0) {
|
||||
parts.push(`${h}h`);
|
||||
}
|
||||
}
|
||||
if (maxUnitIndex >= 1 && minUnitIndex <= 1) {
|
||||
const m = maxUnitIndex === 1 ? minutes : (minutes % 60);
|
||||
if (m > 0) {
|
||||
parts.push(`${m}m`);
|
||||
}
|
||||
}
|
||||
if (maxUnitIndex >= 0) {
|
||||
const s = maxUnitIndex === 0 ? seconds : (seconds % 60);
|
||||
if (s > 0 || parts.length === 0) {
|
||||
parts.push(`${s}s`);
|
||||
const absMs = Math.abs(ms);
|
||||
let result = "";
|
||||
|
||||
for (let i = maxUnitIndex; i >= minUnitIndex; i--) {
|
||||
const value = Math.floor(absMs / durationFactors[i]) % durationSteps[i];
|
||||
if (value > 0) {
|
||||
result += `${value}${shortDurationUnits[i]}`;
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join("");
|
||||
if (!result) {
|
||||
result = `0${shortDurationUnits[minUnitIndex]}`;
|
||||
}
|
||||
|
||||
return ms < 0 ? `-${result}` : result;
|
||||
};
|
||||
|
||||
export const normalizeSnapshotId = (id: string) => {
|
||||
|
||||
@@ -22,7 +22,11 @@ import {
|
||||
Schedule_Clock,
|
||||
type Plan,
|
||||
} from "../../gen/ts/v1/config_pb";
|
||||
import { CalculatorOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
CalculatorOutlined,
|
||||
MinusCircleOutlined,
|
||||
PlusOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { URIAutocomplete } from "../components/URIAutocomplete";
|
||||
import { formatErrorAlert, useAlertApi } from "../components/Alerts";
|
||||
import { namePattern, validateForm } from "../lib/formutil";
|
||||
@@ -551,7 +555,10 @@ const RetentionPolicyView = () => {
|
||||
const retention = Form.useWatch("retention", { form, preserve: true }) as any;
|
||||
// If the first value in the cron expression (minutes) is not just a plain number (e.g. 30), the
|
||||
// cron will hit more than once per hour (e.g. "*/15" "1,30" and "*").
|
||||
const cronIsSubHourly = useMemo(() => schedule?.cron && !/^\d+ /.test(schedule.cron), [schedule?.cron]);
|
||||
const cronIsSubHourly = useMemo(
|
||||
() => schedule?.cron && !/^\d+ /.test(schedule.cron),
|
||||
[schedule?.cron]
|
||||
);
|
||||
// Translates the number of snapshots retained to a retention duration for cron schedules.
|
||||
const minRetention = useMemo(() => {
|
||||
const keepLastN = retention?.policyTimeBucketed?.keepLastN;
|
||||
@@ -567,11 +574,12 @@ const RetentionPolicyView = () => {
|
||||
} else if (schedule?.maxFrequencyDays) {
|
||||
duration = schedule.maxFrequencyDays * (keepLastN - 1) * msPerDay;
|
||||
} else if (schedule?.cron && retention.policyTimeBucketed?.keepLastN) {
|
||||
duration = getMinimumCronDuration(schedule.cron, retention.policyTimeBucketed?.keepLastN);
|
||||
duration = getMinimumCronDuration(
|
||||
schedule.cron,
|
||||
retention.policyTimeBucketed?.keepLastN
|
||||
);
|
||||
}
|
||||
return duration
|
||||
? formatDuration(duration, { maxUnit: "days", minUnit: "minutes" })
|
||||
: null;
|
||||
return duration ? formatDuration(duration, { minUnit: "h" }) : null;
|
||||
}, [schedule, retention?.policyTimeBucketed?.keepLastN]);
|
||||
|
||||
const determineMode = () => {
|
||||
@@ -703,7 +711,8 @@ const RetentionPolicyView = () => {
|
||||
throw new Error("Specify a number greater than 1");
|
||||
}
|
||||
},
|
||||
message: "Your schedule runs more than once per hour; choose how many snapshots to keep before handing off to the retention policy.",
|
||||
message:
|
||||
"Your schedule runs more than once per hour; choose how many snapshots to keep before handing off to the retention policy.",
|
||||
},
|
||||
]}
|
||||
>
|
||||
@@ -711,15 +720,20 @@ const RetentionPolicyView = () => {
|
||||
type="number"
|
||||
min={0}
|
||||
addonAfter={
|
||||
<Tooltip title={minRetention
|
||||
? `${retention?.policyTimeBucketed?.keepLastN} snapshots represents an expected retention duration of at least
|
||||
<Tooltip
|
||||
title={
|
||||
minRetention
|
||||
? `${retention?.policyTimeBucketed?.keepLastN} snapshots represents an expected retention duration of at least
|
||||
${minRetention}, but this may vary with manual backups or if intermittently online.`
|
||||
: "Choose how many snapshots to retain, then use the calculator to see the expected duration they would cover."
|
||||
}>
|
||||
<CalculatorOutlined style={{
|
||||
padding: ".5em",
|
||||
margin: "0 -.5em"
|
||||
}} />
|
||||
: "Choose how many snapshots to retain, then use the calculator to see the expected duration they would cover."
|
||||
}
|
||||
>
|
||||
<CalculatorOutlined
|
||||
style={{
|
||||
padding: ".5em",
|
||||
margin: "0 -.5em",
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user