Skip to content

Conversation

@SylvainChevalier
Copy link
Contributor

@SylvainChevalier SylvainChevalier commented Jan 13, 2026

Summary

Updated tournament timeline components to display the year in dates when the tournament dates differ from the current year.

Previously, years were only shown when start and end dates were in different years, which meant past tournaments didn't show the year.

Fixes #4054

Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved date formatting in tournament timelines. Dates now display more intelligently based on context: when tournament dates occur entirely in the current year, they appear without the year (e.g., "Jan 15"); when spanning multiple years, the full year is included for clarity (e.g., "Jan 15 2024"). This applies to both active and closed tournament views.

✏️ Tip: You can customize this high-level summary in your review settings.

Updated tournament timeline components to display the year in dates
when the tournament dates differ from the current year. Previously,
years were only shown when start and end dates were in different years,
which meant past tournaments didn't show the year.

Fixes #4054

Co-authored-by: Sylvain <[email protected]>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 23, 2026

📝 Walkthrough

Walkthrough

The changes modify date formatting logic in tournament timeline components. They extract year values from various timestamp fields and use composite year comparisons to determine whether dates display with or without the year. When all relevant years match the current year, dates use short format "MMM dd"; otherwise they include the year "MMM dd yyyy".

Changes

Cohort / File(s) Summary
Date Format Logic Updates
front_end/src/app/(main)/(tournaments)/tournament/components/active_tournament_timeline.tsx, front_end/src/app/(main)/(tournaments)/tournament/components/closed_tournament_timeline.tsx
Modified date formatting conditions to show year when tournament dates fall outside the current year. Active timeline checks if both start and close years equal current year; closed timeline additionally requires resolution year to match. Short format "MMM dd" applied only when all relevant years are current, otherwise "MMM dd yyyy".

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 The timeline hops with dates so fine,
Years now dance in a clever design,
When this year blooms, they hide away,
But show their age on a distant day! ✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: displaying years in tournament dates conditionally based on the current year, which is the primary objective of this PR.
Linked Issues check ✅ Passed The PR modifies both active and closed tournament timeline components to display the year in dates when tournament year differs from current year, directly addressing issue #4054's requirement.
Out of Scope Changes check ✅ Passed All changes are focused on updating date formatting logic in tournament timeline components to show years based on current year comparison, remaining within scope of issue #4054.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@front_end/src/app/`(main)/(tournaments)/tournament/components/active_tournament_timeline.tsx:
- Around line 44-50: The year comparison relies on getYear(new Date()) which
uses the server's local timezone; make the intent explicit by computing years in
UTC (or a specific timezone) instead: replace getYear(new Date()), getYear(new
Date(tournament.start_date)), and getYear(new
Date(latestScheduledCloseTimestamp)) with UTC-based year extraction (e.g., new
Date(...).getUTCFullYear()) or, if you need a particular zone, convert dates
with date-fns-tz (utcToZonedTime) and then extract the year; update the
currentYear, startYear, closeYear and consequently formatString logic to use
these UTC/zone-aware year values.
♻️ Duplicate comments (1)
front_end/src/app/(main)/(tournaments)/tournament/components/closed_tournament_timeline.tsx (1)

42-57: Same timezone sensitivity as noted in active timeline.
The currentYear comparison here is also based on server time. If the product requirement is “current year in the user’s locale/time zone,” please ensure the year is derived consistently with the displayed dates.

date-fns getYear timezone behavior (local vs UTC) and recommended approach for SSR date formatting
next-intl server time zone access for formatting dates in the user’s time zone
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f702fe and db33cd9.

📒 Files selected for processing (2)
  • front_end/src/app/(main)/(tournaments)/tournament/components/active_tournament_timeline.tsx
  • front_end/src/app/(main)/(tournaments)/tournament/components/closed_tournament_timeline.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Docker Image / Build Docker Image
  • GitHub Check: Frontend Checks
  • GitHub Check: Backend Checks
  • GitHub Check: integration-tests

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +44 to 50
const currentYear = getYear(new Date());
const startYear = getYear(new Date(tournament.start_date));
const closeYear = getYear(new Date(latestScheduledCloseTimestamp));
const formatString =
getYear(new Date(latestScheduledCloseTimestamp)) ===
getYear(new Date(tournament.start_date))
startYear === currentYear && closeYear === currentYear
? "MMM dd"
: "MMM dd yyyy";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n front_end/src/app/\(main\)/\(tournaments\)/tournament/components/active_tournament_timeline.tsx | head -60

Repository: Metaculus/metaculus

Length of output: 2623


🏁 Script executed:

cat -n front_end/src/app/\(main\)/\(tournaments\)/tournament/components/active_tournament_timeline.tsx | sed -n '51-100p'

Repository: Metaculus/metaculus

Length of output: 114


🏁 Script executed:

# Check how formatDate utility works
cat front_end/src/utils/formatters/date.ts

Repository: Metaculus/metaculus

Length of output: 4243


🏁 Script executed:

# Search for how tournament.start_date is handled elsewhere
rg "tournament.start_date" front_end/src --type ts --type tsx -B2 -A2 | head -50

Repository: Metaculus/metaculus

Length of output: 90


🏁 Script executed:

rg "tournament.start_date" front_end/src -B2 -A2 | head -80

Repository: Metaculus/metaculus

Length of output: 6729


🏁 Script executed:

# Check how the dates are formatted and used in the component
sed -n '44,80p' front_end/src/app/\(main\)/\(tournaments\)/tournament/components/active_tournament_timeline.tsx

Repository: Metaculus/metaculus

Length of output: 1419


🏁 Script executed:

# Check if there are any timezone conversions or locale handling
rg "getYear|format\(" front_end/src/app/\(main\)/\(tournaments\)/tournament/components/active_tournament_timeline.tsx -B1 -A1

Repository: Metaculus/metaculus

Length of output: 920


🏁 Script executed:

sed -n '44,100p' front_end/src/app/\(main\)/\(tournaments\)/tournament/components/active_tournament_timeline.tsx

Repository: Metaculus/metaculus

Length of output: 2359


🏁 Script executed:

# Check the complete file to see how formatString is used
sed -n '75,120p' front_end/src/app/\(main\)/\(tournaments\)/tournament/components/active_tournament_timeline.tsx

Repository: Metaculus/metaculus

Length of output: 1904


🏁 Script executed:

# Check if there's any UTC or timezone conversion happening
rg "UTC|timezone|toISOString|utc" front_end/src/app/\(main\)/\(tournaments\)/tournament/components/active_tournament_timeline.tsx

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Search for how timestamps are created/stored in the backend
rg "latestScheduledCloseTimestamp|start_date" front_end/src/types --type ts -B1 -A1 | head -30

Repository: Metaculus/metaculus

Length of output: 397


🏁 Script executed:

# Check if there are any other similar year comparisons in the codebase
rg "getYear.*new Date" front_end/src --type ts --type tsx | head -20

Repository: Metaculus/metaculus

Length of output: 90


🏁 Script executed:

# Check if dates are ever handled with UTC explicitly
rg "Date\.UTC\|utc\|toUTC\|getUTC" front_end/src --type ts --type tsx | head -20

Repository: Metaculus/metaculus

Length of output: 90


🏁 Script executed:

# Verify if this is truly a server component by checking imports and component signature
head -25 front_end/src/app/\(main\)/\(tournaments\)/tournament/components/active_tournament_timeline.tsx

Repository: Metaculus/metaculus

Length of output: 943


🏁 Script executed:

rg "format.*new Date\|getYear" front_end/src/utils/formatters --type ts -B2 -A2

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check backend to understand what timezone the dates/timestamps use
find backend -name "*.py" -type f | head -5

Repository: Metaculus/metaculus

Length of output: 105


🏁 Script executed:

# Search for any documentation about date handling
rg "timezone\|UTC\|server.*time" . --type md | head -20

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check how formatDate is used in other server components
rg "formatDate.*locale" front_end/src/app --type ts -B1 -A1 | head -30

Repository: Metaculus/metaculus

Length of output: 3048


🏁 Script executed:

# Check if there's a "use client" directive in the file
head -30 front_end/src/app/\(main\)/\(tournaments\)/tournament/components/active_tournament_timeline.tsx | grep -i "use client"

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check if dates passed to the component are ISO strings (UTC) or local strings
rg "Tournament\|latestScheduledCloseTimestamp" front_end/src/types/projects.ts -B3 -A3

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Search for documentation or comments about date handling/timezone
rg "timezone|UTC|local.*time" front_end/src --type ts | head -20

Repository: Metaculus/metaculus

Length of output: 2311


🏁 Script executed:

# Check how the component is called to understand the data flow
rg "ActiveTournamentTimeline" front_end/src --type ts -B2 -A2 | head -40

Repository: Metaculus/metaculus

Length of output: 2251


🏁 Script executed:

# Check the Tournament type more completely
rg "type Tournament|interface Tournament" front_end/src/types --type ts -A 20 | head -50

Repository: Metaculus/metaculus

Length of output: 2971


🏁 Script executed:

# Check how dates come from the API/backend
rg "latestScheduledCloseTimestamp" front_end/src --type ts -B3 -A3 | head -40

Repository: Metaculus/metaculus

Length of output: 4688


🏁 Script executed:

# Verify date-fns getYear behavior with a quick test
node <<'EOF'
const { getYear } = require('date-fns');

// Test 1: ISO string UTC
const date1 = new Date('2024-12-31T23:00:00Z');
console.log('ISO string 2024-12-31T23:00Z:', getYear(date1));

// Test 2: ISO string UTC (next day)
const date2 = new Date('2025-01-01T04:00:00Z');
console.log('ISO string 2025-01-01T04:00Z:', getYear(date2));

// Show what a Date object represents
console.log('Date object for 2024-12-31T23:00:00Z:', date1.toString());
console.log('Date object for 2025-01-01T04:00:00Z:', date2.toString());
EOF

Repository: Metaculus/metaculus

Length of output: 901


🏁 Script executed:

# Find getTimestampFromDateString to understand date format/timezone
rg "getTimestampFromDateString" front_end/src --type ts -B2 -A5 | head -40

Repository: Metaculus/metaculus

Length of output: 4343


🏁 Script executed:

# Check if there's any server timezone configuration
rg "process.env\|TZ\|timezone" front_end/src --type ts | grep -i "tz\|timezone\|env" | head -20

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# See how dates are parsed and what format they're in
rg "new Date\(" front_end/src/app/\(main\)/\(tournaments\)/tournament/components/tournament_timeline.tsx -B1 -A1 | head -30

Repository: Metaculus/metaculus

Length of output: 182


🏁 Script executed:

# Check how other components handle year comparisons or date formatting
rg "getYear" front_end/src --type ts -B2 -A2 | grep -v "node_modules"

Repository: Metaculus/metaculus

Length of output: 4494


🏁 Script executed:

# Verify if dates come from backend as ISO strings (which would be timezone-agnostic)
rg "start_date\|close_date" front_end/src/types/projects.ts -B1 -A1

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check if there's any date-fns timezone handling library in use (like date-fns-tz)
cat front_end/package.json | grep -i "date\|tz" | head -20

Repository: Metaculus/metaculus

Length of output: 117


🏁 Script executed:

# Check if date-fns-tz is actually being used in the codebase
rg "date-fns-tz\|formatInTimeZone\|toDate.*zone" front_end/src --type ts -B1 -A1 | head -30

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check how backend dates are formatted (with or without timezone indicator)
rg "start_date\|close_date.*string" front_end/src/types/projects.ts -B2 -A2

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# See if there's a pattern of UTC handling elsewhere
rg "UTC\|Z\"" front_end/src --type ts | head -20

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check what format the API returns dates in - look for examples in tests or documentation
find front_end -name "*.json" -o -name "*.md" | xargs grep -l "start_date\|close_date" 2>/dev/null | head -5

Repository: Metaculus/metaculus

Length of output: 200


🏁 Script executed:

# See if there's any explicit UTC parsing or timezone handling elsewhere
rg "toUTC\|UTC\|Z\'" front_end/src --type ts | head -10

Repository: Metaculus/metaculus

Length of output: 157


🏁 Script executed:

# Check if similar date comparison patterns exist and if they're addressed anywhere
rg "currentYear.*getYear\|getYear.*new Date\(\)" front_end/src --type ts -B1 -A3

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Look at closed_tournament_timeline to see if it has the same pattern
sed -n '1,80p' front_end/src/app/\(main\)/\(tournaments\)/tournament/components/closed_tournament_timeline.tsx

Repository: Metaculus/metaculus

Length of output: 2862


🏁 Script executed:

# Check what timezone the server runs in or if there's any documentation
find . -name "*.md" -o -name "*.env*" -o -name "next.config.*" | xargs grep -i "tz\|timezone" 2>/dev/null | head -10

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Verify if dates from backend are indeed ISO strings (which are timezone-independent)
rg "2024\|2025" front_end/messages --type json | head -5

Repository: Metaculus/metaculus

Length of output: 45


🌐 Web query:

date-fns getYear timezone behavior UTC local time

💡 Result:

date-fns/getYear(date) returns the calendar year in the runtime’s local (system) time zone, because date-fns operates on plain JavaScript Date objects and (effectively) uses Date.prototype.getFullYear() semantics (year “according to local time”). [1], [2]

What this means in practice

  • A JS Date represents an instant in time, but getFullYear() interprets that instant in the machine’s local time zone. So if your instant is near midnight UTC, converting to local time can push it into the previous/next day/year, changing the year returned by getYear. [2]
  • If you want the UTC year, use Date.prototype.getUTCFullYear() (or the equivalent in your stack), not getYear. [3]

If you need a specific (non-system) time zone

  • With date-fns-tz, you convert between UTC instants and a target zone before extracting/formatting fields; this exists because date-fns returns plain Dates that “implicitly has the current system’s time zone”. [4]
  • In date-fns v4+, you can provide a time-zone “context” using tz(...) from @date-fns/tz so date-fns functions compute in that zone instead of the system zone. [5]

Sources: [1] [2] [3] [4] [5]


🏁 Script executed:

# Check if there are any comments or TODOs about timezone handling
rg "TODO\|FIXME\|timezone\|utc" front_end/src/app/\(main\)/\(tournaments\)/tournament/components/ --type ts

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Look at how the component is actually used and if user timezone info is available
rg "next-intl.*timezone\|useTimeZone\|getTimeZone" front_end/src --type ts | head -10

Repository: Metaculus/metaculus

Length of output: 45


🏁 Script executed:

# Check if dates in the backend are consistently UTC (look for patterns)
rg "datetime\|timestamp" front_end/src/types/projects.ts -B1 -A1 | head -30

Repository: Metaculus/metaculus

Length of output: 45


Year comparison uses server's local timezone; clarify intended reference for consistency.
getYear(new Date()) interprets dates according to the server's local timezone (not UTC). While all dates in this component are processed consistently, the year comparison result depends on the server's deployment timezone. If dates should be compared in UTC or a specific locale, use getUTCFullYear() or date-fns-tz (already installed) to ensure the formatting logic is timezone-explicit and independent of server configuration.

🤖 Prompt for AI Agents
In
`@front_end/src/app/`(main)/(tournaments)/tournament/components/active_tournament_timeline.tsx
around lines 44 - 50, The year comparison relies on getYear(new Date()) which
uses the server's local timezone; make the intent explicit by computing years in
UTC (or a specific timezone) instead: replace getYear(new Date()), getYear(new
Date(tournament.start_date)), and getYear(new
Date(latestScheduledCloseTimestamp)) with UTC-based year extraction (e.g., new
Date(...).getUTCFullYear()) or, if you need a particular zone, convert dates
with date-fns-tz (utcToZonedTime) and then extract the year; update the
currentYear, startYear, closeYear and consequently formatString logic to use
these UTC/zone-aware year values.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 23, 2026

🧹 Preview Environment Cleaned Up

The preview environment for this PR has been destroyed.

Resource Status
🌐 Preview App ✅ Deleted
🗄️ PostgreSQL Branch ✅ Deleted
⚡ Redis Database ✅ Deleted
🔧 GitHub Deployments ✅ Removed
📦 Docker Image ⚠️ Retained (auto-cleanup via GHCR policies)

Cleanup triggered by PR close at 2026-01-23T14:47:27Z

@cemreinanc cemreinanc merged commit dbacc21 into main Jan 23, 2026
16 checks passed
@cemreinanc cemreinanc deleted the claude/issue-4054-20260113-1633 branch January 23, 2026 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tournament dates in the tournament header should show year when in past

3 participants