feat(format): add formatRelativeTime (just now, Xm/Xh/Xd ago, MM/DD/YYYY)
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||||
|
import { formatRelativeTime } from "../format-date";
|
||||||
|
|
||||||
|
describe("formatRelativeTime", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
vi.setSystemTime(new Date("2026-06-17T12:00:00Z"));
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 'just now' for < 45s", () => {
|
||||||
|
expect(formatRelativeTime("2026-06-17T11:59:30Z")).toBe("just now");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns '5m ago' for ~5 minutes", () => {
|
||||||
|
expect(formatRelativeTime("2026-06-17T11:55:00Z")).toBe("5m ago");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns '2h ago' for ~2 hours", () => {
|
||||||
|
expect(formatRelativeTime("2026-06-17T10:00:00Z")).toBe("2h ago");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns 'yesterday' for 1 day", () => {
|
||||||
|
expect(formatRelativeTime("2026-06-16T12:00:00Z")).toBe("yesterday");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns '3d ago' for 3 days", () => {
|
||||||
|
expect(formatRelativeTime("2026-06-14T12:00:00Z")).toBe("3d ago");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns MM/DD/YYYY for > 7 days", () => {
|
||||||
|
expect(formatRelativeTime("2026-06-01T12:00:00Z")).toBe("06/01/2026");
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -36,4 +36,28 @@ export function formatTime(date: string | Date | null | undefined): string {
|
|||||||
hour: "2-digit",
|
hour: "2-digit",
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Just now" / "5m ago" / "2h ago" / "yesterday" / "3d ago" / MM/DD/YYYY.
|
||||||
|
*
|
||||||
|
* Used by the v2 admin Orders list (CardList time-ago badge) and any
|
||||||
|
* other place that needs a humanized, glanceable timestamp without
|
||||||
|
* pulling in a date library. Buckets are deliberately coarse — anything
|
||||||
|
* past a week is shown as the absolute date (MM/DD/YYYY) via formatDate.
|
||||||
|
*/
|
||||||
|
export function formatRelativeTime(input: string | Date | number): string {
|
||||||
|
const date = input instanceof Date ? input : new Date(input);
|
||||||
|
if (isNaN(date.getTime())) return "—";
|
||||||
|
const diffMs = Date.now() - date.getTime();
|
||||||
|
const diffSec = Math.round(diffMs / 1000);
|
||||||
|
if (diffSec < 45) return "just now";
|
||||||
|
const diffMin = Math.round(diffSec / 60);
|
||||||
|
if (diffMin < 60) return `${diffMin}m ago`;
|
||||||
|
const diffHr = Math.round(diffMin / 60);
|
||||||
|
if (diffHr < 24) return `${diffHr}h ago`;
|
||||||
|
const diffDay = Math.round(diffHr / 24);
|
||||||
|
if (diffDay === 1) return "yesterday";
|
||||||
|
if (diffDay < 7) return `${diffDay}d ago`;
|
||||||
|
return formatDate(date);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user