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:
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user