ha-frontend/src/common/util/select-unit.ts

128 lines
2.9 KiB
TypeScript

import { differenceInDays, differenceInWeeks, startOfWeek } from "date-fns/esm";
import { FrontendLocaleData } from "../../data/translation";
import { firstWeekdayIndex } from "../datetime/first_weekday";
export type Unit =
| "second"
| "minute"
| "hour"
| "day"
| "week"
| "month"
| "quarter"
| "year";
const MS_PER_SECOND = 1e3;
const SECS_PER_MIN = 60;
const SECS_PER_HOUR = SECS_PER_MIN * 60;
// Adapted from https://github.com/formatjs/formatjs/blob/186cef62f980ec66252ee232f438a42d0b51b9f9/packages/intl-utils/src/diff.ts
export function selectUnit(
from: Date | number,
to: Date | number = Date.now(),
locale: FrontendLocaleData,
thresholds: Partial<Thresholds> = {}
): { value: number; unit: Unit } {
const resolvedThresholds: Thresholds = {
...DEFAULT_THRESHOLDS,
...(thresholds || {}),
};
const secs = (+from - +to) / MS_PER_SECOND;
if (Math.abs(secs) < resolvedThresholds.second) {
return {
value: Math.round(secs),
unit: "second",
};
}
const mins = secs / SECS_PER_MIN;
if (Math.abs(mins) < resolvedThresholds.minute) {
return {
value: Math.round(mins),
unit: "minute",
};
}
const hours = secs / SECS_PER_HOUR;
if (Math.abs(hours) < resolvedThresholds.hour) {
return {
value: Math.round(hours),
unit: "hour",
};
}
const fromDate = new Date(from);
const toDate = new Date(to);
// Set time component to zero, which allows us to compare only the days
fromDate.setHours(0, 0, 0, 0);
toDate.setHours(0, 0, 0, 0);
const days = differenceInDays(fromDate, toDate);
if (days === 0) {
return {
value: Math.round(hours),
unit: "hour",
};
}
if (Math.abs(days) < resolvedThresholds.day) {
return {
value: days,
unit: "day",
};
}
const firstWeekday = firstWeekdayIndex(locale);
const fromWeek = startOfWeek(fromDate, { weekStartsOn: firstWeekday });
const toWeek = startOfWeek(toDate, { weekStartsOn: firstWeekday });
const weeks = differenceInWeeks(fromWeek, toWeek);
if (weeks === 0) {
return {
value: days,
unit: "day",
};
}
if (Math.abs(weeks) < resolvedThresholds.week) {
return {
value: weeks,
unit: "week",
};
}
const years = fromDate.getFullYear() - toDate.getFullYear();
const months = years * 12 + fromDate.getMonth() - toDate.getMonth();
if (months === 0) {
return {
value: weeks,
unit: "week",
};
}
if (Math.abs(months) < resolvedThresholds.month || years === 0) {
return {
value: months,
unit: "month",
};
}
return {
value: Math.round(years),
unit: "year",
};
}
type Thresholds = Record<
"second" | "minute" | "hour" | "day" | "week" | "month",
number
>;
export const DEFAULT_THRESHOLDS: Thresholds = {
second: 45, // seconds to minute
minute: 45, // minutes to hour
hour: 22, // hour to day
day: 5, // day to week
week: 4, // week to months
month: 11, // month to years
};