fix: display relative time in intel status output (#2132)

* fix: display relative time instead of UTC in intel status output

The `updated_at` timestamps in `gsd-tools intel status` were displayed
as raw ISO/UTC strings, making them appear to show the wrong time in
non-UTC timezones. Replace with fuzzy relative times ("5 minutes ago",
"1 day ago") which are timezone-agnostic and more useful for freshness.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add regression tests for timeAgo utility

Covers boundary values (seconds/minutes/hours/days/months/years),
singular vs plural formatting, and future-date edge case.

Addresses review feedback on #2132.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Andreas Brauchli
2026-04-12 13:54:17 -06:00
committed by GitHub
parent b896db6f91
commit 72af8cd0f7
3 changed files with 137 additions and 1 deletions

View File

@@ -31,6 +31,7 @@ const {
findProjectRoot,
detectSubRepos,
planningDir,
timeAgo,
} = require('../get-shit-done/bin/lib/core.cjs');
// ─── loadConfig ────────────────────────────────────────────────────────────────
@@ -1750,3 +1751,103 @@ describe('planningDir', () => {
);
});
});
// ─── timeAgo ──────────────────────────────────────────────────────────────────
describe('timeAgo', () => {
const now = () => Date.now();
const dateAt = (msAgo) => new Date(now() - msAgo);
// ─── seconds boundary ───
test('returns "just now" for dates under 5 seconds old', () => {
assert.strictEqual(timeAgo(dateAt(0)), 'just now');
assert.strictEqual(timeAgo(dateAt(4_000)), 'just now');
});
test('returns "N seconds ago" between 5 and 59 seconds', () => {
assert.strictEqual(timeAgo(dateAt(5_000)), '5 seconds ago');
assert.strictEqual(timeAgo(dateAt(30_000)), '30 seconds ago');
assert.strictEqual(timeAgo(dateAt(59_000)), '59 seconds ago');
});
// ─── minutes boundary ───
test('transitions to minutes at 60 seconds', () => {
assert.strictEqual(timeAgo(dateAt(60_000)), '1 minute ago');
});
test('uses singular "1 minute ago" for exactly one minute', () => {
assert.strictEqual(timeAgo(dateAt(60_000)), '1 minute ago');
assert.strictEqual(timeAgo(dateAt(119_000)), '1 minute ago');
});
test('uses plural "N minutes ago" for 2-59 minutes', () => {
assert.strictEqual(timeAgo(dateAt(120_000)), '2 minutes ago');
assert.strictEqual(timeAgo(dateAt(5 * 60_000)), '5 minutes ago');
assert.strictEqual(timeAgo(dateAt(59 * 60_000)), '59 minutes ago');
});
// ─── hours boundary ───
test('transitions to hours at 60 minutes', () => {
assert.strictEqual(timeAgo(dateAt(60 * 60_000)), '1 hour ago');
});
test('uses singular "1 hour ago" for exactly one hour', () => {
assert.strictEqual(timeAgo(dateAt(60 * 60_000)), '1 hour ago');
assert.strictEqual(timeAgo(dateAt(119 * 60_000)), '1 hour ago');
});
test('uses plural "N hours ago" for 2-23 hours', () => {
assert.strictEqual(timeAgo(dateAt(2 * 3600_000)), '2 hours ago');
assert.strictEqual(timeAgo(dateAt(23 * 3600_000)), '23 hours ago');
});
// ─── days boundary ───
test('transitions to days at 24 hours', () => {
assert.strictEqual(timeAgo(dateAt(24 * 3600_000)), '1 day ago');
});
test('uses singular "1 day ago" for exactly one day', () => {
assert.strictEqual(timeAgo(dateAt(24 * 3600_000)), '1 day ago');
});
test('uses plural "N days ago" for 2-29 days', () => {
assert.strictEqual(timeAgo(dateAt(2 * 86400_000)), '2 days ago');
assert.strictEqual(timeAgo(dateAt(29 * 86400_000)), '29 days ago');
});
// ─── months boundary ───
test('transitions to months at 30 days', () => {
assert.strictEqual(timeAgo(dateAt(30 * 86400_000)), '1 month ago');
});
test('uses singular "1 month ago" for exactly one month (30 days)', () => {
assert.strictEqual(timeAgo(dateAt(30 * 86400_000)), '1 month ago');
assert.strictEqual(timeAgo(dateAt(59 * 86400_000)), '1 month ago');
});
test('uses plural "N months ago" for 2-11 months', () => {
assert.strictEqual(timeAgo(dateAt(60 * 86400_000)), '2 months ago');
assert.strictEqual(timeAgo(dateAt(180 * 86400_000)), '6 months ago');
});
// ─── years boundary ───
test('transitions to years at 365 days', () => {
assert.strictEqual(timeAgo(dateAt(365 * 86400_000)), '1 year ago');
});
test('uses singular "1 year ago" for exactly one year', () => {
assert.strictEqual(timeAgo(dateAt(365 * 86400_000)), '1 year ago');
});
test('uses plural "N years ago" for 2+ years', () => {
assert.strictEqual(timeAgo(dateAt(2 * 365 * 86400_000)), '2 years ago');
assert.strictEqual(timeAgo(dateAt(10 * 365 * 86400_000)), '10 years ago');
});
// ─── edge cases ───
test('handles future dates as "just now" (negative elapsed)', () => {
// A date 5 seconds in the future has negative elapsed time, which floors to a negative
// number of seconds and hits the "under 5 seconds" branch.
assert.strictEqual(timeAgo(new Date(Date.now() + 5_000)), 'just now');
});
});