Files
Checkmate/client/src/Utils/TimeUtils.ts
T

105 lines
2.8 KiB
TypeScript
Raw Normal View History

2026-01-23 23:54:58 +00:00
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
import customParseFormat from "dayjs/plugin/customParseFormat";
2026-07-31 11:55:05 -07:00
import prettyMilliseconds from "pretty-ms";
2026-01-23 23:54:58 +00:00
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(customParseFormat);
dayjs.extend(duration);
export const MS_PER_SECOND = 1000;
export const MS_PER_MINUTE = 60 * MS_PER_SECOND;
export const MS_PER_HOUR = 60 * MS_PER_MINUTE;
export const MS_PER_DAY = 24 * MS_PER_HOUR;
export const MS_PER_WEEK = MS_PER_DAY * 7;
const resolveSystemTimezone = (): string => {
try {
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
} catch {
return "UTC";
}
};
export const SYSTEM_TIMEZONE = resolveSystemTimezone();
export const resolveTimezone = (preferred?: string | null): string =>
preferred?.trim() || SYSTEM_TIMEZONE;
export const formatDateWithTz = (
timestamp: string,
format: string,
timezone?: string
) => {
2026-01-23 23:54:58 +00:00
if (!timestamp) {
return "Unknown time";
}
const tz = resolveTimezone(timezone);
try {
return dayjs(timestamp).tz(tz).format(format);
} catch {
return dayjs(timestamp).utc().format(format);
}
2026-01-23 23:54:58 +00:00
};
export const tickDateFormatLookup = (range: string) => {
const tickFormatLookup: Record<string, string> = {
2026-01-26 02:06:37 +00:00
recent: "h:mm A",
day: "h:mm A",
week: "MM/D, h:mm A",
month: "ddd. M/D",
2026-01-23 23:54:58 +00:00
};
const format = tickFormatLookup[range];
if (format === undefined) {
return "";
}
return format;
};
export const tooltipDateFormatLookup = (range: string) => {
const dateFormatLookup: Record<string, string> = {
2026-01-26 02:06:37 +00:00
recent: "ddd. MMMM D, YYYY, hh:mm A",
day: "ddd. MMMM D, YYYY, hh:mm A",
week: "ddd. MMMM D, YYYY, hh:mm A",
month: "ddd. MMMM D, YYYY",
2026-01-23 23:54:58 +00:00
};
const format = dateFormatLookup[range];
if (format === undefined) {
return "";
}
return format;
};
2026-02-05 21:40:36 +00:00
export const formatTimestamp = (timestamp: string | number | null): string => {
if (!timestamp) return "-";
const date = new Date(timestamp);
return date.toLocaleString();
};
2026-07-31 11:55:05 -07:00
const DISPLAY_UNITS = [MS_PER_DAY, MS_PER_HOUR, MS_PER_MINUTE, MS_PER_SECOND];
// pretty-ms floors seconds-and-above and truncates fractional ms, so values
// must be rounded to their display unit first to land on the nearest whole
// unit (53.6 → "54 ms", 1937 → "2 s", 59500 → "1 m").
const roundToDisplayUnit = (ms: number): number => {
for (const unit of DISPLAY_UNITS) {
if (ms >= unit) return Math.round(ms / unit) * unit;
}
return ms >= 1 ? Math.round(ms) : ms;
};
export const formatMs = (ms: number, hasSpace: boolean = true): string => {
const formatted = prettyMilliseconds(roundToDisplayUnit(ms), {
2026-07-31 11:55:05 -07:00
compact: true,
formatSubMilliseconds: true,
secondsDecimalDigits: 0,
});
if (hasSpace) {
return formatted.replace(/^(\d+)(\D+)$/, "$1 $2");
}
return formatted;
2026-07-31 11:55:05 -07:00
};