Files
job-search/web/src/lib/format.ts
T

89 lines
2.6 KiB
TypeScript

import type { DateTimeString } from '@/types/Opportunities';
const ZERO_TIME_PREFIX = '0001-01-01';
const dateFormat = new Intl.DateTimeFormat('en-GB', {
day: 'numeric',
month: 'short',
year: 'numeric',
});
const dateTimeFormat = new Intl.DateTimeFormat('en-GB', {
day: 'numeric',
month: 'short',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
const moneyFormat = new Intl.NumberFormat('en-GB', {
style: 'currency',
currency: 'GBP',
maximumFractionDigits: 0,
});
// en-GB's compact suffixes are k, m and bn; amounts under £1,000 are left
// whole.
const shortMoneyFormat = new Intl.NumberFormat('en-GB', {
style: 'currency',
currency: 'GBP',
notation: 'compact',
});
const countFormat = new Intl.NumberFormat('en-GB');
/** Treats null and Go's zero time.Time as "no date". */
export function parseDate(value: DateTimeString | null | undefined): Date | null {
if (!value || value.startsWith(ZERO_TIME_PREFIX)) return null;
const d = new Date(value);
return Number.isNaN(d.getTime()) ? null : d;
}
/** A date for display, e.g. "3 Mar 2026", or a dash for no date. */
export function formatDate(value: DateTimeString | null | undefined): string {
const d = parseDate(value);
return d ? dateFormat.format(d) : '—';
}
/** A date and time for display, e.g. "3 Mar 2026, 09:30", or a dash for none. */
export function formatDateTime(value: DateTimeString | null | undefined): string {
const d = parseDate(value);
return d ? dateTimeFormat.format(d) : '—';
}
/** An amount in pounds for display, e.g. "£125,001", or a dash for none. */
export function formatMoney(value: number): string {
return value > 0 ? moneyFormat.format(value) : '—';
}
/**
* An amount in pounds shortened with a suffix, e.g. "£250k", "£1.5m" or
* "£36bn", or a dash for none. Amounts under £1,000 are shown whole.
*/
export function formatMoneyShort(value: number): string {
return value > 0 ? shortMoneyFormat.format(value) : '—';
}
/** A count for display, e.g. "4,854". */
export function formatCount(value: number): string {
return countFormat.format(value);
}
/**
* An amount in its currency for display, e.g. "€1,201", or a dash for none.
* A currency code Intl doesn't know is shown after the number.
*/
export function formatValue(amount: number, currency: string): string {
if (amount <= 0) return '—';
if (currency === '' || currency === 'GBP') return moneyFormat.format(amount);
try {
return new Intl.NumberFormat('en-GB', {
style: 'currency',
currency,
maximumFractionDigits: 0,
}).format(amount);
} catch {
return `${countFormat.format(Math.round(amount))} ${currency}`;
}
}