Skip to content

Give the site one definition of a calendar date - #288

Merged
dmccoystephenson merged 3 commits into
mainfrom
fix/one-date-formatter
Aug 10, 2026
Merged

Give the site one definition of a calendar date#288
dmccoystephenson merged 3 commits into
mainfrom
fix/one-date-formatter

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

Four call sites still formatted their own dates instead of using the shared helper. All four now call absoluteDateFrom from utils/relativeTime.ts.

absoluteDateFrom was already pinned to en-US and UTC and already returns '' rather than Invalid Date for an unparseable value, so no new logic was needed — only its doc comment changed, because it no longer describes something the News page owns.

Visible change: the three profile/API-key dates go from 1/2/2026 to January 2, 2026. That is intended — it is how the rest of the site prints a date. The footer's output is unchanged for any date it was already formatting correctly.

Test plan

  • npm run lint — no warnings or errors
  • npm test — 246 tests across 37 files pass
  • npm run build — clean; /account and /u/[username] still build static, /resources/[slug] still server-rendered
  • absoluteDateFrom's existing tests already cover the behaviour relied on here: UTC pinning either side of midnight (__tests__/relativeTime.test.ts:45-46) and '' for a malformed input (line 50)
  • Footer reads "N visits since January 2, 2026" as before

Closes #286
Closes #287

The footer, both "Member since" lines and the API-key list each formatted
their own date. The footer picked up the runtime's time zone, so a counter
started near midnight UTC rendered as one day on the server and another in
the browser; the other three passed no locale either, so a join date read
1/2/2026 or 02/01/2026 depending on the visitor.

All four now call absoluteDateFrom, which is already pinned to en-US and
UTC and already returns '' rather than "Invalid Date" for a malformed
value. Its comment no longer describes it as the News page's formatter,
because it is now the site's.

Closes #286
Closes #287

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@dmccoystephenson dmccoystephenson left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review: the substitution itself is sound — absoluteDateFrom is a strict improvement at all four sites, and I confirmed by grep that no other calendar-date formatter remains in the codebase, so the new comment's "only calendar-date formatter" claim holds. One real edge case found in the footer, noted inline. The three profile/API-key sites have no equivalent problem: each interpolates into a label (Member since, Created) already conditional on the object existing, and their createdAt comes from an API contract rather than a writable data file.

Comment thread components/BottomBar.tsx Outdated
month: 'long',
day: 'numeric'
}) : null;
const formattedDate = startDate ? absoluteDateFrom(startDate) : null;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes what an unparseable startDate renders, and the surrounding guard was not updated to match.

The old inline formatter returned the string "Invalid Date" for a bad value. absoluteDateFrom returns ''. But the clause below (line 59) is still guarded on startDate being truthy, not on the formatted result — so a malformed date now leaves the footer reading 5 visits since with nothing after it: a dangling preposition rather than a visibly wrong date.

This is reachable rather than theoretical: startDate traces back to data/visits.json, a runtime-persisted file bind-mounted in compose.yml and therefore editable outside the app. Neither rendering is good, but an empty trailing clause reads as a broken template, while "Invalid Date" at least announced itself.

Suggest guarding on the formatted value instead — {visits != null && formattedDate && ( on line 59 — so an unparseable date hides the counter entirely rather than half a sentence. That would also make this line's startDate ? ternary redundant, since absoluteDateFrom already returns '' for an empty or malformed input.

dmccoystephenson and others added 2 commits August 9, 2026 22:14
The counter was guarded on startDate being present, but rendered
formattedDate. The old formatter returned "Invalid Date" for a bad value,
so something was always printed; absoluteDateFrom returns '', which left
the footer reading "5 visits since " with nothing after it.

Guard on the formatted result instead, so an unparseable date hides the
counter rather than half a sentence. data/visits.json is bind-mounted and
editable outside the app, so this is reachable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dmccoystephenson
dmccoystephenson merged commit 63b8c7a into main Aug 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant