DateTime: middle tier shows relative time only — move the "ordinal of Month" into the hover tooltip #312

Closed
opened 2026-09-10 22:11:58 +00:00 by crueber · 3 comments
Owner

What's requested

The date component's middle tier currently renders "3 days ago - 2nd of September" — the visible text should become just the relative portion ("3 days ago"), with the "{ordinal} of {Month}" part moved into the hover tooltip (prepended to the existing local-time tooltip).

Current implementation (code evidence)

  • fmtDate (web/src/lib/format.js:193-206): the 1–30-day tier returns `${plural(days, "day")} - ${monthDay}` where monthDay = "{ordinal} of {Month}".
  • <DateTime> (web/src/components/DateTime.jsx) renders fmtDate as visible text and fmtDateTitle (local wall time "YYYY-MM-DD HH:MM CDT") as the title — the title currently has no calendar date, which is why the ordinal/month lives in the visible text today.
  • The 31+ days tier (absolute, "September 28th, 2024") is unaffected — it's already fully absolute.
  • The design comments at format.js:131-158 document the tier contract and law-12 decisions; the visible-text change and the tooltip change are both format.js-internal (one new helper, one amended tier, one amended title) — DateTime.jsx needs no change.

Proposed change

  1. fmtDate middle tier → return only plural(days, "day") (e.g. "3 days ago"). The monthDay string moves out of the visible text.
  2. fmtDateTitle → prepend the calendar date to the existing local-time tooltip for timestamps in the 1–30-day window: "{Month} {ordinal} · YYYY-MM-DD HH:MM <zone>" (e.g. "September 2nd · 2025-09-02 14:03 CDT"). Exact separator is the implementer's call — keep it terse and consistent. The 31+-day tier keeps its existing title (its visible text already carries the full date).
  3. Tests: format.js is headless-tested (there are existing fmtDate/fmtDateTitle unit tests per the repo's headless-module rule) — update the middle-tier expectations and add title assertions for both tiers' boundary cases (exactly 1 day, 30 days, 31 days).

Acceptance criteria

  • Visible date text for 1–30-day-old timestamps is relative only ("3 days ago") — no " - 2nd of September" suffix.
  • Hovering shows the calendar date (ordinal + month) in the tooltip alongside the existing local wall time.
  • < 1 day and 31+ day tiers render exactly as today (no regression).
  • Headless unit tests updated and passing (node --test web/test/unit/*.test.js).
## What's requested The date component's middle tier currently renders `"3 days ago - 2nd of September"` — the visible text should become **just the relative portion** (`"3 days ago"`), with the `"{ordinal} of {Month}"` part moved into the hover tooltip (prepended to the existing local-time tooltip). ## Current implementation (code evidence) - `fmtDate` (`web/src/lib/format.js:193-206`): the 1–30-day tier returns `` `${plural(days, "day")} - ${monthDay}` `` where `monthDay = "{ordinal} of {Month}"`. - `<DateTime>` (`web/src/components/DateTime.jsx`) renders `fmtDate` as visible text and `fmtDateTitle` (local wall time "YYYY-MM-DD HH:MM CDT") as the `title` — the title currently has **no calendar date**, which is why the ordinal/month lives in the visible text today. - The 31+ days tier (absolute, "September 28th, 2024") is unaffected — it's already fully absolute. - The design comments at `format.js:131-158` document the tier contract and law-12 decisions; the visible-text change and the tooltip change are both `format.js`-internal (one new helper, one amended tier, one amended title) — `DateTime.jsx` needs no change. ## Proposed change 1. **`fmtDate` middle tier** → return only `plural(days, "day")` (e.g. `"3 days ago"`). The `monthDay` string moves out of the visible text. 2. **`fmtDateTitle`** → prepend the calendar date to the existing local-time tooltip for timestamps in the 1–30-day window: `"{Month} {ordinal} · YYYY-MM-DD HH:MM <zone>"` (e.g. `"September 2nd · 2025-09-02 14:03 CDT"`). Exact separator is the implementer's call — keep it terse and consistent. The 31+-day tier keeps its existing title (its visible text already carries the full date). 3. **Tests**: `format.js` is headless-tested (there are existing `fmtDate`/`fmtDateTitle` unit tests per the repo's headless-module rule) — update the middle-tier expectations and add title assertions for both tiers' boundary cases (exactly 1 day, 30 days, 31 days). ## Acceptance criteria - [ ] Visible date text for 1–30-day-old timestamps is relative only ("3 days ago") — no " - 2nd of September" suffix. - [ ] Hovering shows the calendar date (ordinal + month) in the tooltip alongside the existing local wall time. - [ ] < 1 day and 31+ day tiers render exactly as today (no regression). - [ ] Headless unit tests updated and passing (`node --test web/test/unit/*.test.js`).
crueber added this to the v1 milestone 2026-09-10 22:27:06 +00:00
Author
Owner

Fix ready for review: PR #316 (#316, branch fix/issue-312). Middle tier renders relative-only; tooltip prepends "{Month} {ordinal} · wall time" in the 1–30d window; 31+ day tier untouched. Web unit suite 596/596 green.

Fix ready for review: PR #316 (https://git.packden.us/crueber/walhub/pulls/316, branch fix/issue-312). Middle tier renders relative-only; tooltip prepends "{Month} {ordinal} · wall time" in the 1–30d window; 31+ day tier untouched. Web unit suite 596/596 green.
Author
Owner

Review of PR #316 (fix/issue-312, commit f6de1e3) — verified in scratch worktree /tmp/pr316 (removed afterward); main worktree untouched (still clean on main).

FINDINGS (all checked, no defects):

  • web/src/lib/format.js:207 — middle tier returns plural(days,'day') only; no ' - ' remnant. Verified live: 1d/3d/30d render '1 day ago'/'3 days ago'/'30 days ago' with zero ' - ' substring. RESOLVED.
  • web/src/lib/format.js:250-251 — title prefix gated on days>=1 && days<=30, computed identically to fmtDate's tier (floor(max(0,now-t)/DAY)). Boundary pins verified live with pinned now: 23h plain, 1d PREFIX, 30d PREFIX, 31d plain, 400d plain; sub-day and 31+ unchanged. RESOLVED.
  • web/src/lib/format.js:234-236 + :208 — monthOrdinal() shared by the 31+-day absolute form and the title prefix, so spellings cannot drift; absolute output identical to before ('August 10th, 2025' shape: Month ordinal, Year). RESOLVED.
  • Ordinals spot-checked: 1st 2nd 3rd 4th 11th 12th 13th 21st 22nd 23rd 31st all correct (ordinal() untouched). RESOLVED.
  • Title separator '·' ('September 9th · 2025-09-09 12:00 UTC') is terse and matches the issue's proposed '{Month} {ordinal} · wall' format, consistent with the 31+ tier word order. RESOLVED.
  • web/test/unit/dates.test.js:123-127 — new title test pins now (const now=Date.now(); fmtDateTitle(iso,now)); no flakes. fmtDate agoIso() has ms slack but floor-stable at day boundaries. RESOLVED.
  • web/src/components/DateTime.jsx:17 — render logic unchanged (single-arg fmtDateTitle(v())/fmtDate(v()), defaults supply Date.now()); only design comments updated, matching the issue's 'DateTime.jsx needs no change' for logic. RESOLVED.
  • docs/go/12_web_ui.md — #133 entry updated in the same change (law 12): middle-tier relative-only, title-prefix window, boundary notes, and headless-cover line naming the 1/30/31-day pins; accurate vs code+tests. RESOLVED.
  • No new deps: diff is exactly 4 files (format.js, DateTime.jsx comments, dates.test.js, 12_web_ui.md). Laws 1/7/8/12 hold. RESOLVED.

TESTS (scratch worktree, node_modules symlinked from main checkout since worktrees don't carry ignored dirs):

  • web/test/unit/dates.test.js: 13/13 pass.
  • All non-smoke unit files in two batches: 282/282 + 311/311 pass (593 total, 0 fail).
  • smoke.test.js not runnable in this sandbox (top-level fetch to 127.0.0.1:8080 hangs instead of refusing/skipping; file untouched by this PR) — noted explicitly, no browser needed per review instructions and none used.
  • vite build in web/: success (141 modules, 2.33s).

No fixes pushed (nothing to fix).

MERGE RECOMMENDATION: ready to merge.

Review of PR #316 (fix/issue-312, commit f6de1e3) — verified in scratch worktree /tmp/pr316 (removed afterward); main worktree untouched (still clean on main). FINDINGS (all checked, no defects): - web/src/lib/format.js:207 — middle tier returns plural(days,'day') only; no ' - ' remnant. Verified live: 1d/3d/30d render '1 day ago'/'3 days ago'/'30 days ago' with zero ' - ' substring. RESOLVED. - web/src/lib/format.js:250-251 — title prefix gated on days>=1 && days<=30, computed identically to fmtDate's tier (floor(max(0,now-t)/DAY)). Boundary pins verified live with pinned now: 23h plain, 1d PREFIX, 30d PREFIX, 31d plain, 400d plain; sub-day and 31+ unchanged. RESOLVED. - web/src/lib/format.js:234-236 + :208 — monthOrdinal() shared by the 31+-day absolute form and the title prefix, so spellings cannot drift; absolute output identical to before ('August 10th, 2025' shape: Month ordinal, Year). RESOLVED. - Ordinals spot-checked: 1st 2nd 3rd 4th 11th 12th 13th 21st 22nd 23rd 31st all correct (ordinal() untouched). RESOLVED. - Title separator '·' ('September 9th · 2025-09-09 12:00 UTC') is terse and matches the issue's proposed '{Month} {ordinal} · wall' format, consistent with the 31+ tier word order. RESOLVED. - web/test/unit/dates.test.js:123-127 — new title test pins now (const now=Date.now(); fmtDateTitle(iso,now)); no flakes. fmtDate agoIso() has ms slack but floor-stable at day boundaries. RESOLVED. - web/src/components/DateTime.jsx:17 — render logic unchanged (single-arg fmtDateTitle(v())/fmtDate(v()), defaults supply Date.now()); only design comments updated, matching the issue's 'DateTime.jsx needs no change' for logic. RESOLVED. - docs/go/12_web_ui.md — #133 entry updated in the same change (law 12): middle-tier relative-only, title-prefix window, boundary notes, and headless-cover line naming the 1/30/31-day pins; accurate vs code+tests. RESOLVED. - No new deps: diff is exactly 4 files (format.js, DateTime.jsx comments, dates.test.js, 12_web_ui.md). Laws 1/7/8/12 hold. RESOLVED. TESTS (scratch worktree, node_modules symlinked from main checkout since worktrees don't carry ignored dirs): - web/test/unit/dates.test.js: 13/13 pass. - All non-smoke unit files in two batches: 282/282 + 311/311 pass (593 total, 0 fail). - smoke.test.js not runnable in this sandbox (top-level fetch to 127.0.0.1:8080 hangs instead of refusing/skipping; file untouched by this PR) — noted explicitly, no browser needed per review instructions and none used. - vite build in web/: success (141 modules, 2.33s). No fixes pushed (nothing to fix). MERGE RECOMMENDATION: ready to merge.
Author
Owner

Fixed by PR #316 (review clean; tier + tooltip boundaries verified; all green), merged. Closing.

Fixed by PR #316 (review clean; tier + tooltip boundaries verified; all green), merged. Closing.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#312
No description provided.