Skip to content

fix(dashboards): Align time series X axis ticks to user timezone#109881

Draft
gggritso wants to merge 12 commits into
masterfrom
georgegritsouk/fix/chart-timezone-offset-problem
Draft

fix(dashboards): Align time series X axis ticks to user timezone#109881
gggritso wants to merge 12 commits into
masterfrom
georgegritsouk/fix/chart-timezone-offset-problem

Conversation

@gggritso
Copy link
Copy Markdown
Member

@gggritso gggritso commented Mar 4, 2026

Compute timezone-aware tick positions for time series chart X axes using ECharts' customValues, so ticks always land on round boundaries in the user's configured timezone.

Before:
Screenshot 2026-03-04 at 10 40 32 AM

After:
Screenshot 2026-03-04 at 10 40 37 AM

ECharts can only snap ticks to browser-local or UTC boundaries. When a user's Sentry timezone differs from their browser timezone, ticks appear at non-round times (e.g., "9:30 PM" instead of "12:00 AM"). This replaces the built-in tick placement with a custom algorithm that snaps to round boundaries in the user's timezone, paired with a cascading formatter that produces mixed-granularity labels (e.g., "2025 | Feb | Mar | 2:00 PM").

The custom algorithm uses a flat single-pass approach (pick one interval, snap, walk forward) rather than ECharts' multi-level hierarchy. Mixed-granularity formatting is handled separately by formatXAxisTimestamp, which inspects each tick and cascades through format levels based on what round boundary it falls on. This produces the same visual result as ECharts' built-in hierarchy.

Note: ECharts 6.0.0 has a bug (#21226) where customValues ticks crash in leveledFormat because they lack a .time property. The fix is merged upstream (PR #21352) but not yet released. Local patches to node_modules are applied as a workaround — these should be made permanent via pnpm patch or removed once a new ECharts version ships.

Tl:DR we are awaiting ECharts 6.1.0!

Closes DAIN-594

gggritso and others added 8 commits March 3, 2026 18:01
ECharts can only place ticks at browser-local or UTC boundaries. When
the user's Sentry timezone setting differs from their browser timezone,
ticks appear at non-round times (e.g., "9:00 PM" on every tick instead
of midnight). Use ECharts' `customValues` feature to compute tick
positions at round boundaries in the user's configured timezone.

Adds a `generateTimezoneAlignedTicks` utility that mirrors ECharts'
interval selection algorithm but uses moment-timezone to snap ticks to
the correct timezone. No data shifting is needed — timestamps stay as
real UTC everywhere, so zoom, tooltips, and release bubbles are
unaffected.

Refs DAIN-594
Co-Authored-By: Claude <noreply@anthropic.com>
Track tick generation duration via Sentry.metrics.distribution to
monitor performance in production. Tags include the chosen interval
unit and tick count.

Refs DAIN-594
Co-Authored-By: Claude <noreply@anthropic.com>
Separate the snapped date computation into its own variable to make
the day boundary snapping logic easier to read.

Refs DAIN-594
Co-Authored-By: Claude <noreply@anthropic.com>
…ation

Add a visual test story with 21 charts covering standard time ranges,
DST transitions, and edge cases. The story overrides the user timezone
to Asia/Kolkata to simulate a browser/Sentry timezone mismatch, verifying
that ticks land on round boundaries. Also removes axisLabel.customValues
to work around an ECharts 6.0.0 bug in leveledFormat.
…ensity

The X axis label formatter was parsing tick values in browser-local
time, but generateTimezoneAlignedTicks places ticks at round
boundaries in the user's configured timezone. This caused labels like
"10:30 AM" (browser-local) instead of "12:00 AM" (user timezone).

Add a timezone parameter to formatXAxisTimestamp so it interprets
tick values in the correct timezone via moment-timezone. Also bump
splitNumber from 5 to 10 to match ECharts' default tick density,
and revert the broken type:'value' axis workaround. Improve code
comments explaining the flat tick generation + cascading formatter
approach.

Co-Authored-By: Claude <noreply@anthropic.com>
…ver stories

Change the year-boundary label format from "Jan 1st 2025" to just
"2025" for a cleaner axis when crossing year boundaries. Add story
test cases for day, month, and year boundary rollover to demonstrate
the cascading formatter producing mixed-granularity labels. Bold
the chart titles for better readability.

Co-Authored-By: Claude <noreply@anthropic.com>
Simplify formatXAxisTimestamp to accept (value, timezone) instead of
an options object. Restructure generateTimezoneAlignedTicks with the
main function at top and helpers below. Clean up test files with
helpers at bottom and array-style parameterized tests. Extract
makeRandomWalkTimeSeries to its own file and test cases to a constant
in the story.

Co-Authored-By: Claude <noreply@anthropic.com>
@gggritso gggritso changed the title Fix X-axis timezone alignment for time series charts fix(dashboards): Align time series X axis ticks to user timezone Mar 4, 2026
@github-actions github-actions Bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Mar 4, 2026
@linear
Copy link
Copy Markdown

linear Bot commented Mar 4, 2026

Remove export from getParser in dates.tsx since it's only used
internally. Use `attributes` instead of `tags` in Sentry metrics
distribution call to match the MetricOptions type.

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

This comment was marked as outdated.

@getsantry getsantry Bot added the Stale label Mar 26, 2026
@gggritso gggritso removed the Stale label Mar 27, 2026
Resolved conflict in timeSeriesWidgetVisualization.tsx: combined
React imports from both branches (useEffect + useState from master,
useMemo from our branch; Fragment removed as it's no longer used).
@getsantry

This comment was marked as outdated.

@getsantry getsantry Bot added the Stale label Apr 24, 2026
@gggritso gggritso removed the Stale label Apr 24, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 11, 2026

📊 Type Coverage Diff

✅ No new type safety issues introduced. Coverage: 93.47%

…ignature change

- Replace `return undefined` with bare `return` to satisfy unicorn/no-useless-undefined
- Fix default import of ConfigStore to named import (no default export)
- Update HeatMapWidgetVisualization to use the new `timezone: string` signature of formatXAxisTimestamp instead of the old `{ utc: boolean | undefined }` object

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant