Files
probo/packages/i18n/duration.ts
Jonathan a7ff5f07bc Add react i18next to console
Signed-off-by: Jonathan <contact@grafikart.fr>
2026-07-27 11:52:31 +02:00

97 lines
3.3 KiB
TypeScript

// Copyright (c) 2026 Probo Inc <hello@probo.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
type Translator = (key: string, options?: { count?: number }) => string;
const DURATION_UNITS = [
{ value: "seconds", seconds: 1, snap: 0 },
{ value: "minutes", seconds: 60, snap: 5 },
{ value: "hours", seconds: 3_600, snap: 5 * 60 },
{ value: "days", seconds: 86_400, snap: 2 * 3_600 },
{ value: "weeks", seconds: 604_800, snap: 12 * 3_600 },
{ value: "months", seconds: 2_592_000, snap: 2 * 24 * 3_600 },
{ value: "years", seconds: 31_536_000, snap: 21 * 24 * 3_600 },
] as const;
export function humanizeSeconds(
seconds: number | null,
t: Translator,
): string {
if (seconds === null || seconds <= 0) {
return '';
}
let remaining = seconds;
const parts: string[] = [];
for (const { value, seconds: durationInSeconds, snap } of [...DURATION_UNITS].reverse()) {
if (remaining >= durationInSeconds - snap) {
let count = Math.floor(remaining / durationInSeconds);
const leftover = remaining - count * durationInSeconds;
if (leftover >= durationInSeconds - snap) {
count++;
remaining = 0;
} else if (leftover <= snap) {
remaining = 0;
} else {
remaining = leftover;
}
parts.push(`${count} ${t(`duration.${value}`, { count })}`);
}
}
return parts.length > 0 ? parts.join(", ") : t("duration.session");
}
export function formatDuration(
duration?: string | null,
t?: Translator,
): string | null {
if (!duration || !t) return null;
const timeMatch = duration.match(/PT(\d+)([MH])/);
if (timeMatch) {
const amount = parseInt(timeMatch[1], 10) || 0;
const unit = timeMatch[2];
if (unit === "M") return t("duration.min", { count: amount });
if (unit === "H") return t("duration.hour", { count: amount });
}
const dateMatch = duration.match(/P(\d+)([DW])/);
if (dateMatch) {
const amount = parseInt(dateMatch[1], 10) || 0;
const unit = dateMatch[2];
if (unit === "W") {
return `${amount} ${amount === 1 ? t("Week") : t("Weeks")}`;
}
if (unit === "D") {
if (amount % 7 === 0 && amount > 0) {
const weeks = amount / 7;
return `${weeks} ${weeks === 1 ? t("Week") : t("Weeks")}`;
}
return `${amount} ${amount === 1 ? t("Day") : t("Days")}`;
}
}
return null;
}