import { type TimestampFormat } from "@t3tools/contracts/settings"; export function getTimestampFormatOptions( timestampFormat: TimestampFormat, includeSeconds: boolean,): Intl.DateTimeFormatOptions { const baseOptions: Intl.DateTimeFormatOptions = { hour: "numeric", minute: "2-digit", ...(includeSeconds ? { second: "2-digit" } : {}), }; if (timestampFormat === "locale") { return baseOptions; } return { ...baseOptions, hour12: timestampFormat === "12-hour", };} const timestampFormatterCache = new Map<string, Intl.DateTimeFormat>(); function getTimestampFormatter( timestampFormat: TimestampFormat, includeSeconds: boolean,): Intl.DateTimeFormat { const cacheKey = `${timestampFormat}:${includeSeconds ? "seconds" : "minutes"}`; const cachedFormatter = timestampFormatterCache.get(cacheKey); if (cachedFormatter) { return cachedFormatter; } const formatter = new Intl.DateTimeFormat( undefined, getTimestampFormatOptions(timestampFormat, includeSeconds), ); timestampFormatterCache.set(cacheKey, formatter); return formatter;} export function formatTimestamp(isoDate: string, timestampFormat: TimestampFormat): string { return getTimestampFormatter(timestampFormat, true).format(new Date(isoDate));} export function formatShortTimestamp(isoDate: string, timestampFormat: TimestampFormat): string { return getTimestampFormatter(timestampFormat, false).format(new Date(isoDate));} /** * Format a relative time string from an ISO date. * Returns `{ value: "20s", suffix: "ago" }` or `{ value: "just now", suffix: null }` * so callers can style the numeric portion independently. */export function formatRelativeTime(isoDate: string): { value: string; suffix: string | null } { const diffMs = Date.now() - new Date(isoDate).getTime(); if (diffMs < 0) return { value: "just now", suffix: null }; const seconds = Math.floor(diffMs / 1000); if (seconds < 5) return { value: "just now", suffix: null }; if (seconds < 60) return { value: `${seconds}s`, suffix: "ago" }; const minutes = Math.floor(seconds / 60); if (minutes < 60) return { value: `${minutes}m`, suffix: "ago" }; const hours = Math.floor(minutes / 60); if (hours < 24) return { value: `${hours}h`, suffix: "ago" }; const days = Math.floor(hours / 24); return { value: `${days}d`, suffix: "ago" };} export function formatRelativeTimeLabel(isoDate: string) { const relative = formatRelativeTime(isoDate); return relative.suffix ? `${relative.value} ${relative.suffix}` : relative.value;}
import { type TimestampFormat } from "@t3tools/contracts/settings";
export function getTimestampFormatOptions(
timestampFormat: TimestampFormat,
includeSeconds: boolean,
): Intl.DateTimeFormatOptions {
const baseOptions: Intl.DateTimeFormatOptions = {
hour: "numeric",
minute: "2-digit",
...(includeSeconds ? { second: "2-digit" } : {}),
};
if (timestampFormat === "locale") {
return baseOptions;
}
return {
...baseOptions,
hour12: timestampFormat === "12-hour",
};
}
const timestampFormatterCache = new Map<string, Intl.DateTimeFormat>();
function getTimestampFormatter(
timestampFormat: TimestampFormat,
includeSeconds: boolean,
): Intl.DateTimeFormat {
const cacheKey = `${timestampFormat}:${includeSeconds ? "seconds" : "minutes"}`;
const cachedFormatter = timestampFormatterCache.get(cacheKey);
if (cachedFormatter) {
return cachedFormatter;
}
const formatter = new Intl.DateTimeFormat(
undefined,
getTimestampFormatOptions(timestampFormat, includeSeconds),
);
timestampFormatterCache.set(cacheKey, formatter);
return formatter;
}
export function formatTimestamp(isoDate: string, timestampFormat: TimestampFormat): string {
return getTimestampFormatter(timestampFormat, true).format(new Date(isoDate));
}
export function formatShortTimestamp(isoDate: string, timestampFormat: TimestampFormat): string {
return getTimestampFormatter(timestampFormat, false).format(new Date(isoDate));
}
/**
* Format a relative time string from an ISO date.
* Returns `{ value: "20s", suffix: "ago" }` or `{ value: "just now", suffix: null }`
* so callers can style the numeric portion independently.
*/
export function formatRelativeTime(isoDate: string): { value: string; suffix: string | null } {
const diffMs = Date.now() - new Date(isoDate).getTime();
if (diffMs < 0) return { value: "just now", suffix: null };
const seconds = Math.floor(diffMs / 1000);
if (seconds < 5) return { value: "just now", suffix: null };
if (seconds < 60) return { value: `${seconds}s`, suffix: "ago" };
const minutes = Math.floor(seconds / 60);
if (minutes < 60) return { value: `${minutes}m`, suffix: "ago" };
const hours = Math.floor(minutes / 60);
if (hours < 24) return { value: `${hours}h`, suffix: "ago" };
const days = Math.floor(hours / 24);
return { value: `${days}d`, suffix: "ago" };
}
export function formatRelativeTimeLabel(isoDate: string) {
const relative = formatRelativeTime(isoDate);
return relative.suffix ? `${relative.value} ${relative.suffix}` : relative.value;
}