fix: use local timezone for all user-facing datetime displays#1906
fix: use local timezone for all user-facing datetime displays#1906amikofalvy wants to merge 5 commits intomainfrom
Conversation
Follow-up to the hydration fix (6fb0715) and initial traces fix (73d79bd). Ensures all remaining datetime renders show local time instead of UTC: - evaluation run config results, evaluation job results - dataset items table, dataset run details - GitHub installation details page - MCP server details (removed local formatDate, use shared utility) - GitHub installations list (removed local formatDate, use shared utility) - Trigger invocations table (removed local formatDate, use shared utility) - Created LocalDateTimeTable client component for server component contexts Co-authored-by: Cursor <cursoragent@cursor.com>
🦋 Changeset detectedLatest commit: 91539e5 The changes in this PR will be included in the next version bump. This PR includes changesets to release 10 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
|
No docs changes detected. This PR is an internal UI bug fix for datetime display formatting (UTC → local timezone) that doesn't impact any public APIs, SDK interfaces, or documented features. |
- external-agents/view-external-agent-details.tsx: replaced local formatDate
with shared formatDateTimeTable + { local: true }
- evaluation-jobs/evaluation-jobs-list.tsx: replaced toLocaleDateString calls
with shared formatDate + { local: true } (hydration safety)
- evaluations/jobs/[configId]/page.tsx: replaced toLocaleDateString() with
shared formatDate (consistent server-side formatting)
- github/[installationId]/page.tsx: added { local: true } to repo.createdAt
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
PR Review Summary
2 Key Findings | Risk: Medium
🔴❗ Critical (1) ❗🔴
- 🔴 Critical:
.changeset/fix-local-timestamps-everywhere.md:1-3Changeset uses incorrect package name — will likely fail during release
🟠⚠️ Major (1) 🟠⚠️
- 🟠 Major:
evaluation-jobs-list.tsx:174Missing{ local: true }for updatedAt timestamp
🟡 Minor (1) 🟡
🟡 1) multi-file Several other formatDate calls not updated in this "all remaining" sweep
Issue: The PR title says "use local timezone for all user-facing datetime displays" and the description mentions "applies the same pattern to all remaining datetime renders." However, the consistency review found 12+ other files with formatDate() calls that still don't have { local: true }:
agents/agent-item.tsx:53—formatDate(createdAt)api-keys/expiration-indicator.tsx:48,58—formatDate(expiresAt)datasets/dataset-item.tsx:37—formatDate(createdAt)projects/project-item.tsx:51—formatDate(createdAt)data-components/data-component-item.tsx:40—formatDate(createdAt)external-agents/external-agent-item.tsx:164—formatDate(externalAgent.createdAt)credentials/credential-item.tsx:135—formatDate(createdAt)evaluators/evaluators-list.tsx:103—formatDate(evaluator.updatedAt)artifact-components/artifact-component-item.tsx:42—formatDate(createdAt)mcp-servers/mcp-tool-item.tsx:205—formatDate(tool.createdAt)evaluation-run-configs/evaluation-run-configs-list.tsx:141—formatDate(runConfig.updatedAt)
Why: If these are intentionally out of scope (e.g., item cards use date-only format where UTC vs local is less noticeable), that's reasonable — but the PR title suggests a comprehensive sweep.
Fix: Either:
- Update these files to use
{ local: true }for completeness, or - Adjust the PR title/description to clarify scope (e.g., "fix local timezone for datetime displays on detail pages")
Refs:
- PR description — "applies the same pattern to all remaining datetime renders"
💭 Consider (1) 💭
💭 1) formatDateAgo Consider adding { local: true } to formatDateAgo calls
Issue: The formatDateAgo utility also supports { local: true } but calls throughout the codebase don't use it. For recent timestamps (showing "3h ago"), timezone doesn't matter. But for older dates (>30 days), the fallback renders an absolute date in UTC.
Why: Lower priority since relative time is timezone-agnostic, but worth considering for consistency.
Fix: Add { local: true } to formatDateAgo() calls, or document that fallback dates intentionally use UTC.
💡 APPROVE WITH SUGGESTIONS
Summary: The approach is sound and the pattern application is correct, but there are two issues that should be addressed: (1) the changeset uses the wrong package name which will likely cause it to be ignored during release, and (2) one formatDate call in a file that was already modified was missed. Both have 1-click suggestion fixes in the inline comments above. The minor finding about other files is for the author to decide on scope.
Discarded (4)
| Location | Issue | Reason Discarded |
|---|---|---|
local-datetime.tsx |
Missing className prop for extensibility |
Too nitpicky for a simple utility component; can be added later if needed |
evaluations/jobs/[configId]/page.tsx:34-35 |
Server component formatDate without { local: true } |
This is in a server component for date-only display; UTC rendering may be intentional for consistency with how date ranges are stored |
formatDateAgo fallback dates in multiple files |
formatDateAgo calls without { local: true } |
Flagged as "Consider" instead — relative time is timezone-agnostic, only fallback to absolute dates is affected |
evaluation-jobs-list.tsx:98-99 |
Already uses { local: true } |
Positive observation, not an issue |
Reviewers (4)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
pr-review-standards |
1 | 0 | 0 | 0 | 1 | 0 | 0 |
pr-review-frontend |
1 | 0 | 0 | 0 | 1 | 0 | 0 |
pr-review-consistency |
4 | 1 | 1 | 0 | 0 | 0 | 2 |
pr-review-devops |
2 | 0 | 0 | 0 | 1 | 0 | 1 |
| Total | 8 | 1 | 1 | 0 | 2 | 0 | 3 |
Note: The evaluation-jobs-list.tsx:174 issue was identified by multiple reviewers (standards, frontend, consistency) and merged into a single inline comment.
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
There was a problem hiding this comment.
PR Review Summary
0 Key Findings | Risk: Low
This is a delta review scoped to changes since the last automated review (fd42417a → eaf1248f).
Delta Changes
The only change in this delta was applying the 1-click suggestion to fix the changeset package name:
'agents-manage-ui'→"@inkeep/agents-manage-ui"✅
The CRITICAL issue from the prior review is now resolved.
🕐 Pending Recommendations (1)
- 🟠
evaluation-jobs-list.tsx:174Missing{ local: true }forupdatedAttimestamp in table row
💭 Consider (1)
💭 1) meta Author feedback on changeset workflow
Issue: The author noted that the original changeset file was created manually instead of using the pnpm bump script, which led to the incorrect package name format.
Why: The pnpm bump script ensures correct package names, proper formatting, and consistent changeset structure. Manual creation bypasses these guardrails.
Fix: This is feedback for future AI agent sessions — when creating changesets, always use:
pnpm bump <patch|minor|major> --pkg <package> "<message>"Refs:
- Author's comment — "The agent should have been using the
bumppnpm script" - CLAUDE.md changeset section — already documents
pnpm bumpas the correct workflow
💡 APPROVE WITH SUGGESTIONS
Summary: The delta successfully addresses the CRITICAL changeset issue from the prior review. One MAJOR issue remains pending from the prior review (missing { local: true } at line 174 in evaluation-jobs-list.tsx). The author may choose to address this or scope it out intentionally. Overall, the PR is in good shape for the intended purpose.
Discarded (0)
No items discarded in this delta review.
Reviewers (0)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
| Total | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Note: No sub-agent reviewers dispatched — delta was a single changeset fix that didn't require specialized review.
The { local: true } option causes hydration mismatches when used in client
components that receive data from server components via props. During SSR,
Intl.DateTimeFormat defaults to UTC (Vercel's server timezone), but the
client renders local time — React keeps the server-rendered UTC version.
This replaces direct { local: true } calls with LocalDateTime wrapper
components that:
1. Initially render UTC (matching server HTML — no hydration mismatch)
2. Switch to local time after mount via useEffect
Components with client-side data fetching (useState/useEffect) are
unaffected and keep using { local: true } directly since timestamps
only render after mount.
Affected files (SSR'd from server component props):
- invocations-table.tsx → LocalDateTime
- dataset-items-table.tsx → LocalDateTimeTable
- evaluation-run-config-results.tsx → LocalDateTimeTable
- evaluation-job-results.tsx → LocalDateTimeTable
- view-external-agent-details.tsx → LocalDateTimeTable
- view-mcp-server-details-user-scope.tsx → LocalDateTimeTable
- view-mcp-server-details-project-scope.tsx → LocalDateTimeTable
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
PR Review Summary
0 Key Findings | Risk: Low
This is a delta review scoped to changes since the last automated review (eaf1248f → a49c747b).
Delta Changes
The delta contains a single commit (a49c747b) that introduces a proper hydration-safe pattern for local timezone rendering:
local-datetime.tsx refactor:
- Changed from a simple passthrough wrapper to a
useState/useEffectpattern - Initial render uses UTC (matching server-rendered HTML)
- Client-side
useEffectupdates to local time after mount - Exports four variants:
LocalDate,LocalDateTime,LocalDateTimeTable,LocalDateAgo
Component migrations (7 files):
- Switched from
formatDateTime*(..., { local: true })to using the new wrapper components - This ensures hydration-safe behavior when data comes from SSR'd server components
Both pr-review-standards and pr-review-frontend reviewers found no issues with the delta implementation. The hydration-safe pattern is correctly implemented and follows React/Next.js best practices.
🕐 Pending Recommendations (1)
- 🟠
evaluation-jobs-list.tsx:174Missing{ local: true }forformatDate(jobConfig.updatedAt)— this line was flagged in the initial review and remains unaddressed (the file was not part of this delta)
💡 APPROVE WITH SUGGESTIONS
Summary: The delta successfully implements a hydration-safe pattern for local timezone rendering — well done! 🎉 The LocalDateTimeBase component correctly uses useState/useEffect to avoid hydration mismatches. One MAJOR issue from the prior review remains pending at evaluation-jobs-list.tsx:174, which the author may address in a follow-up or scope out intentionally.
Discarded (0)
No items discarded in this delta review.
Reviewers (2)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
pr-review-frontend |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
pr-review-standards |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Total | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Note: Clean delta — both reviewers validated the hydration-safe pattern implementation with no concerns.
The previous useState/useEffect approach may not have been updating the DOM in all cases. This new approach uses three layers: 1. Client renders local time directly (no useEffect delay) 2. suppressHydrationWarning accepts server/client text mismatch 3. useEffect + ref.textContent as a DOM patching fallback Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
PR Review Summary
0 Key Findings | Risk: Low
This is a delta review scoped to changes since the last automated review (a49c747b → 91539e52).
Delta Changes
The delta contains a single commit (91539e52) that refactors the LocalDateTimeBase component in local-datetime.tsx:
Previous implementation (flash of UTC):
- Used
useState/useEffectpattern - Initial render showed UTC (matching server HTML)
useEffectupdated state to local time after mount- User briefly saw UTC before switching to local
New implementation (immediate local time):
- Uses
suppressHydrationWarningto allow client to immediately render local time - Adds
useRef/useEffectfallback that patches DOM textContent if React's hydration doesn't update it - No flash of UTC — user sees local time immediately
Both pr-review-frontend and pr-review-standards reviewers found no issues with the delta. The suppressHydrationWarning pattern is a standard React approach for timezone-dependent content where server/client mismatch is intentional. The useEffect fallback is a reasonable defensive measure.
🕐 Pending Recommendations (1)
- 🟠
evaluation-jobs-list.tsx:174Missing{ local: true }forformatDate(jobConfig.updatedAt)— this was flagged in the initial review and remains unaddressed (the file was not modified in this delta)
✅ APPROVE
Summary: The delta successfully improves the hydration-safe pattern for local timezone rendering — users now see local time immediately instead of a brief flash of UTC. The implementation is clean and follows React best practices. 🎉 One MAJOR issue remains pending from prior reviews (evaluation-jobs-list.tsx:174), which the author may choose to address in this PR or scope out intentionally.
Discarded (0)
No items discarded in this delta review.
Reviewers (2)
| Reviewer | Returned | Main Findings | Consider | While You're Here | Inline Comments | Pending Recs | Discarded |
|---|---|---|---|---|---|---|---|
pr-review-frontend |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
pr-review-standards |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
| Total | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
Note: Clean delta — both reviewers validated the suppressHydrationWarning + useRef/useEffect fallback pattern with no concerns.
| const ref = useRef<HTMLSpanElement>(null); | ||
| const localDisplay = fn(dateString, { local: true }); | ||
|
|
||
| // Fallback: ensure DOM is updated to local time after mount |
There was a problem hiding this comment.
Claude finished @shagun-singh-inkeep's task in 47s —— View job
Todo List
- Read and analyze the local-datetime.tsx implementation
- Evaluate the necessity of the useRef/useEffect fallback
- Provide detailed explanation
Analysis: Is the fallback necessary?
Short answer: No, the fallback is not necessary and can be safely removed.
Why the fallback isn't needed
When using suppressHydrationWarning on a React element:
-
React handles the content update: During hydration, React detects the mismatch between server HTML (UTC time) and client render (local time). With
suppressHydrationWarning, React suppresses the warning but still updates the DOM to match the client-rendered content. -
The fallback never executes: The condition
ref.current.textContent !== localDisplaywill never be true after React's hydration completes because React has already updated the text content to matchlocalDisplay.
How suppressHydrationWarning works
<span suppressHydrationWarning>
{localDisplay} // Client renders local time immediately
</span>- Server renders: UTC time (e.g., "2024-01-15 14:30 UTC")
- Client hydrates: Local time (e.g., "2024-01-15 09:30 EST")
- React's behavior: Updates DOM to local time, suppresses the hydration warning
Proof the fallback is redundant
The useEffect check:
if (ref.current && ref.current.textContent !== localDisplay) {
ref.current.textContent = localDisplay; // This never executes
}By the time useEffect runs (after mount), React has already reconciled the DOM. The text content will already match localDisplay, making the condition always false.
Recommended simplification
Remove lines 33, 36-41:
function LocalDateTimeBase({ dateString, format }: { dateString: string; format: FormatFn }) {
const fn = formatFns[format];
const localDisplay = fn(dateString, { local: true });
return (
<span suppressHydrationWarning>
{localDisplay}
</span>
);
}This simpler version works identically because suppressHydrationWarning already ensures React handles the server/client mismatch properly.
Edge case consideration
The only theoretical scenario where a fallback might be useful is if there's a React bug where suppressHydrationWarning fails to update the DOM. However:
- This would be a critical React bug affecting many applications
- No such issues are documented in React 18/19
- The current fallback wouldn't reliably fix such a bug anyway
Recommendation: Remove the unnecessary fallback for cleaner, simpler code. The suppressHydrationWarning prop alone is the correct and sufficient solution.
| ); | ||
| } | ||
|
|
||
| export function LocalDate({ dateString }: { dateString: string }) { |
There was a problem hiding this comment.
this function is dead code i believe
| return <LocalDateTimeBase dateString={dateString} format="dateTimeTable" />; | ||
| } | ||
|
|
||
| export function LocalDateAgo({ dateString }: { dateString: string }) { |
There was a problem hiding this comment.
this function is also dead code i believe
| return <LocalDateTimeBase dateString={dateString} format="date" />; | ||
| } | ||
|
|
||
| export function LocalDateTime({ dateString }: { dateString: string }) { |
There was a problem hiding this comment.
since this is only used in invocations-table.tsx and that file is a 'use client' we can just use formatDateTime(date, { local: true }) right? Or I may be misunderstanding the point of this function
Summary
Follow-up to the hydration fix (#1863 /
6fb0715) and initial traces local-time fix (#1903 /73d79bd). The hydration fix forced allIntl.DateTimeFormatcalls to UTC, but this made timestamps across the UI display in UTC instead of the user's local timezone. The traces fix added{ local: true }opt-in for the traces area; this PR applies the same pattern to all remaining datetime renders.Changes
High priority —
formatDateTime/formatDateTimeTablecalls displaying specific times in UTC (now local):evaluation-run-config-results.tsx— conversation created timestampsevaluation-job-results.tsx— conversation created timestampsdataset-items-table.tsx— item updated timestampsruns/[runId]/page.tsx— run and conversation created timestampsgithub/[installationId]/page.tsx— installation created/updated timestampsServer component fix:
evaluations/jobs/[configId]/page.tsx— created aLocalDateTimeTableclient component since{ local: true }has no effect in server components (server always renders UTC on Vercel)Replaced local
formatDateimplementations with shared utility (fixes hydration risk):view-mcp-server-details-shared.tsx— removed localformatDateusingtoLocaleDateString(), updated callers (view-mcp-server-details-user-scope.tsx,view-mcp-server-details-project-scope.tsx) to useformatDateTimeTablefrom shared utility with{ local: true }work-app-github-installations-list.tsx— removed localformatDate, now imports sharedformatDatewith{ local: true }triggers/invocations-table.tsx— removed localformatDate, now imports sharedformatDateTimewith{ local: true }Test plan
Made with Cursor