4044de7bfc
Build and push image / build (push) Successful in 56s
Dates were computed using browser/server local time with no explicit timezone, causing inconsistencies when server runs in UTC. Now all "today" computations and date formatting use the user's chosen IANA timezone, persisted in localStorage and selectable from Settings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import { getToday } from "./tz.js";
|
|
|
|
function parseDate(s: string): Date {
|
|
return new Date(s.length === 10 ? s + "T12:00:00" : s);
|
|
}
|
|
|
|
export const fmt = {
|
|
g(n: number | null | undefined): string {
|
|
if (n == null) return "—";
|
|
const trimmed = (+n).toFixed(2).replace(/\.?0+$/, "");
|
|
return `${trimmed || "0"} g`;
|
|
},
|
|
money(n: number | null | undefined): string {
|
|
if (n == null) return "—";
|
|
return `$${(+n).toFixed(2)}`;
|
|
},
|
|
moneyShort(n: number | null | undefined): string {
|
|
if (n == null) return "—";
|
|
return n >= 100 ? `$${Math.round(n)}` : `$${(+n).toFixed(2)}`;
|
|
},
|
|
pct(n: number | null | undefined): string {
|
|
if (n == null) return "—";
|
|
return `${(+n).toFixed(1)}%`;
|
|
},
|
|
date(s: string | null | undefined, tz?: string): string {
|
|
if (!s) return "—";
|
|
const opts: Intl.DateTimeFormatOptions = { month: "short", day: "numeric", year: "numeric" };
|
|
if (tz) opts.timeZone = tz;
|
|
return parseDate(s).toLocaleDateString("en-US", opts);
|
|
},
|
|
dateShort(s: string | null | undefined, tz?: string): string {
|
|
if (!s) return "—";
|
|
const opts: Intl.DateTimeFormatOptions = { month: "short", day: "numeric" };
|
|
if (tz) opts.timeZone = tz;
|
|
return parseDate(s).toLocaleDateString("en-US", opts);
|
|
},
|
|
daysAgo(s: string | null | undefined, tz?: string): string {
|
|
if (!s) return "—";
|
|
const todayMs = +parseDate(tz ? getToday(tz) : new Date().toISOString().slice(0, 10));
|
|
const thenMs = +parseDate(s);
|
|
const d = Math.floor((todayMs - thenMs) / 86_400_000);
|
|
if (d === 0) return "today";
|
|
if (d === 1) return "yesterday";
|
|
if (d < 0) return "in the future";
|
|
if (d < 30) return `${d}d ago`;
|
|
if (d < 365) return `${Math.floor(d / 30)}mo ago`;
|
|
return `${Math.floor(d / 365)}y ago`;
|
|
},
|
|
};
|
|
|
|
export const TYPE_GLYPHS: Record<string, string> = {
|
|
Flower: "✿",
|
|
Concentrate: "◆",
|
|
Edible: "◐",
|
|
Vaporizer: "▢",
|
|
"Pre-roll": "│",
|
|
Tincture: "◯",
|
|
};
|