Timestamp and registration checking
This commit is contained in:
118
src/lib/format.ts
Normal file
118
src/lib/format.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* Locale-aware date/time formatting helpers.
|
||||
*
|
||||
* Built on top of next-intl's format API, which wraps Intl.DateTimeFormat /
|
||||
* Intl.RelativeTimeFormat using the active request locale. These helpers add
|
||||
* three things on top of raw next-intl:
|
||||
*
|
||||
* 1. Tolerant input — accepts string | Date | null | undefined and returns
|
||||
* a stable em-dash for missing values, so call sites don't need to
|
||||
* conditionally render.
|
||||
* 2. Two presets used everywhere in the portal (`dateTime`, `dateOnly`)
|
||||
* so the four locales render consistently. German/French/Italian use
|
||||
* 24h DD.MM.YYYY HH:mm; English uses 12h MMM D, YYYY h:mm a.
|
||||
* 3. A `relative` helper that auto-picks the right unit (minute/hour/day/
|
||||
* week/month) based on the elapsed delta.
|
||||
*
|
||||
* Usage in client components:
|
||||
*
|
||||
* import { useFormatter } from "next-intl";
|
||||
* import { formatDateTime, formatRelative } from "@/lib/format";
|
||||
*
|
||||
* const f = useFormatter();
|
||||
* <span>{formatDateTime(req.createdAt, f)}</span>
|
||||
* <span title={formatDateTime(req.createdAt, f)}>
|
||||
* {formatRelative(req.createdAt, f)}
|
||||
* </span>
|
||||
*
|
||||
* Usage in server components:
|
||||
*
|
||||
* import { getFormatter } from "next-intl/server";
|
||||
* const f = await getFormatter();
|
||||
* ...same calls...
|
||||
*/
|
||||
|
||||
// next-intl's `useFormatter()` (client) and `getFormatter()` (server) return
|
||||
// the same shape. We derive the type from useFormatter's return so we stay
|
||||
// in sync with next-intl version bumps without hand-maintaining a mirror.
|
||||
import type { useFormatter } from "next-intl";
|
||||
type Formatter = ReturnType<typeof useFormatter>;
|
||||
|
||||
const FALLBACK = "—";
|
||||
|
||||
function toDate(value: string | Date | null | undefined): Date | null {
|
||||
if (!value) return null;
|
||||
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
|
||||
const d = new Date(value);
|
||||
return Number.isNaN(d.getTime()) ? null : d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full date+time, locale-formatted. Returns "—" if the value is missing.
|
||||
*
|
||||
* de: 25.04.2026, 14:30
|
||||
* en: Apr 25, 2026, 2:30 PM
|
||||
* fr: 25 avr. 2026, 14:30
|
||||
* it: 25 apr 2026, 14:30
|
||||
*/
|
||||
export function formatDateTime(
|
||||
value: string | Date | null | undefined,
|
||||
formatter: Formatter
|
||||
): string {
|
||||
const d = toDate(value);
|
||||
if (!d) return FALLBACK;
|
||||
return formatter.dateTime(d, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Date only, locale-formatted. Use in dense table cells.
|
||||
*/
|
||||
export function formatDateOnly(
|
||||
value: string | Date | null | undefined,
|
||||
formatter: Formatter
|
||||
): string {
|
||||
const d = toDate(value);
|
||||
if (!d) return FALLBACK;
|
||||
return formatter.dateTime(d, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Relative time ("2 hours ago", "vor 2 Stunden", etc.).
|
||||
* Picks the unit automatically based on the magnitude of the delta.
|
||||
* Returns "—" if the value is missing.
|
||||
*
|
||||
* Anchors against `now` (defaults to current time) so SSR and client
|
||||
* render the same string when called within a single request.
|
||||
*/
|
||||
export function formatRelative(
|
||||
value: string | Date | null | undefined,
|
||||
formatter: Formatter,
|
||||
now: Date = new Date()
|
||||
): string {
|
||||
const d = toDate(value);
|
||||
if (!d) return FALLBACK;
|
||||
|
||||
const diffMs = d.getTime() - now.getTime();
|
||||
const absSeconds = Math.abs(diffMs) / 1000;
|
||||
|
||||
let unit: Intl.RelativeTimeFormatUnit;
|
||||
if (absSeconds < 60) unit = "second";
|
||||
else if (absSeconds < 3_600) unit = "minute";
|
||||
else if (absSeconds < 86_400) unit = "hour";
|
||||
else if (absSeconds < 604_800) unit = "day";
|
||||
else if (absSeconds < 2_592_000) unit = "week";
|
||||
else if (absSeconds < 31_536_000) unit = "month";
|
||||
else unit = "year";
|
||||
|
||||
return formatter.relativeTime(d, { now, unit });
|
||||
}
|
||||
Reference in New Issue
Block a user