Skip to content

fix(echarts): floor sub-second noise out of no-grain smart date x-axis labels - #44701

Open
kokhlo wants to merge 5 commits into
apache:masterfrom
kokhlo:fix/44698-smart-date-ms-label
Open

kokhlo wants to merge 5 commits into
apache:masterfrom
kokhlo:fix/44698-smart-date-ms-label

Conversation

@kokhlo

@kokhlo kokhlo commented Sep 26, 2026

Copy link
Copy Markdown
Contributor

SUMMARY

Fixes #44698 (claim comment with the full RCA: #44698 (comment))

On a temporal x-axis with no time grain (e.g. the bundled Top 10 Games example, whose temporal year BIGINT column is used directly with no time_grain_sqla), the last axis label renders as .943ms instead of a year.

Root cause chain, verified on main:

  1. getXAxisFormatter(xAxisTimeFormat, resolvedTimeGrain) → with no grain, getSmartDateFormatter(undefined) returns the raw smart_date formatter — the setMilliseconds(0) normalization exists only inside the grain-aware wrapper (plugins/plugin-chart-echarts/src/utils/formatters.ts).
  2. smart_date's finest tier is millisecond: '.%Lms' (packages/superset-ui-core/src/time-format/formatters/smartDate.ts).
  3. ECharts pads the time-axis extent past the last data point, and the forced showMaxLabel boundary label formats the padded value — which carries sub-second noise. Confirmed against the repo's own echarts: the forced max label receives values like 2009-01-01T00:00:00.943Z, rendering .943ms while every real tick is a clean year.

This regressed when #38017/#41350 replaced #38083's grain-agnostic floor-to-second axis wrapper with the grain-aware normalizer — the no-grain path lost the protection.

BEFORE/AFTER

  • before: formatter(new Date('2009-01-01T00:00:00.943Z')) → ".943ms"
  • after: → "2009" (identical to the clean 2009-01-01T00:00:00Z tick)

TESTING

  • new regression test in test/utils/formatters.test.ts: RED on main (.943ms vs 2009), GREEN with the fix
  • full local gates: formatters.test.ts 24 passed, test/Timeseries 14 suites / 384 passed, test/MixedTimeseries 4 suites / 65 passed, oxfmt + oxlint clean

ADDITIONAL INFORMATION

@bito-code-review

bito-code-review Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #d8d8e3

Actionable Suggestions - 0
Additional Suggestions - 1
  • superset-frontend/plugins/plugin-chart-echarts/src/utils/formatters.ts - 1
    • Duplicated ms-flooring logic · Line 50-58
      The new no-grain branch duplicates logic the existing wrapper already implements: with `timeGrain` undefined, no `TimeGranularity` branch matches, so the wrapper zeroes milliseconds and calls `baseFormatter(normalizedDate)` — identical to this branch. Removing the early return keeps one flooring implementation and prevents the two copies from diverging.
Review Details
  • Files reviewed - 2 · Commit Range: 1ff9ac4..1ff9ac4
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/formatters.ts
    • superset-frontend/plugins/plugin-chart-echarts/test/utils/formatters.test.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +49 to +58
if (!timeGrain) {
return baseFormatter;
return new TimeFormatter({
id: SMART_DATE_ID,
label: baseFormatter.label,
formatFunc: (date: Date) => {
const floored = new Date(date);
floored.setMilliseconds(0);
return baseFormatter(floored);
},
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggestion: With no grain, legitimate sub-second timestamps are floored before formatting, so distinct ticks within one second receive identical labels and lose their only displayed precision.

Assessment: 🟠 Major · 🔁 Occurrence: Sometimes · 🏷️ Logic error

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/plugins/plugin-chart-echarts/src/utils/formatters.ts
**Line:** 49:58
**Comment:**
	*Logic Error: With no grain, legitimate sub-second timestamps are floored before formatting, so distinct ticks within one second receive identical labels and lose their only displayed precision.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of 784b0fb.

When no time grain is provided, the function returns the base formatter before creating or normalizing a date, preserving sub-second timestamps.

If that's not right, unresolve this thread and CodeAnt will leave it open.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ CodeAnt verified this suggestion was addressed in subsequent commits and marked this thread resolved as of d1f8501.

When no time grain is provided, the function returns the base formatter before creating or normalizing a date, preserving sub-second timestamps.

If that's not right, unresolve this thread and CodeAnt will leave it open.

@bito-code-review

Copy link
Copy Markdown
Contributor

The flagged issue is correct. The current implementation in the PR explicitly floors the date to the second by setting milliseconds to zero, which causes distinct timestamps within the same second to be formatted identically, losing sub-second precision.

To resolve this while still avoiding the millisecond tier for axis-boundary noise, you should remove the forced flooring and instead configure the formatter to ignore sub-second precision only when it is not explicitly required. A better approach is to ensure the formatter's adaptive logic does not trigger the millisecond tier for these specific boundary labels, or to use a format that does not include milliseconds by default.

Since there are no other comments in this PR, no further actions are required.

superset-frontend/plugins/plugin-chart-echarts/src/utils/formatters.ts

if (!timeGrain) {
    return new TimeFormatter({
      id: SMART_DATE_ID,
      label: baseFormatter.label,
      formatFunc: (date: Date) => baseFormatter(date),
    });
  }

@codecov

codecov Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.83333% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 82.32%. Comparing base (7a2913e) to head (784b0fb).

Files with missing lines Patch % Lines
...ugins/plugin-chart-echarts/src/utils/formatters.ts 92.30% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           master   #44701       +/-   ##
===========================================
+ Coverage   57.31%   82.32%   +25.00%     
===========================================
  Files        2997     2997               
  Lines      185294   185302        +8     
  Branches    42888    42896        +8     
===========================================
+ Hits       106198   152544    +46346     
+ Misses      78056    29996    -48060     
- Partials     1040     2762     +1722     
Flag Coverage Δ
javascript 77.98% <95.83%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@kokhlo

kokhlo commented Sep 27, 2026

Copy link
Copy Markdown
Contributor Author

Valid point — verified the duplication is exact: with timeGrain undefined no TimeGranularity branch matches, so the wrapper's fall-through already zeroes milliseconds and calls baseFormatter on the floored date, byte-identical to the removed early return. Rather than extracting a shared helper for two consecutive branches in one function, dropped the early return in 9023d01 and let the no-grain case fall through the existing wrapper (the comment there now explains why). The regression test is unchanged and still pins the behavior; full formatters.test.ts 24/24 locally.

@bito-code-review

bito-code-review Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #48ecc4

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: 1ff9ac4..9023d01
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/formatters.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@sadpandajoe
sadpandajoe requested a review from msyavuz September 28, 2026 17:25
@rusackas

Copy link
Copy Markdown
Member

Thanks for the writeup on the root cause here, @kokhlo, that's a solid RCA. One thing before this merges though: codeant's still-open thread on formatters.ts has a point that bito's own follow-up comment raised too. Falling through to the grain-aware wrapper zeroes milliseconds for every tick in the no-grain path, not just the padded axis-boundary label that was actually broken, so a real ungrained series with genuine sub-second timestamps would now render distinct ticks identically. Feels like the fix should target the boundary label specifically rather than flooring every tick.

@github-actions github-actions Bot added the requires:rebase Requires rebasing on top of current master label Sep 30, 2026
@kokhlo
kokhlo force-pushed the fix/44698-smart-date-ms-label branch from 9023d01 to 9cf2b29 Compare September 30, 2026 05:32
@kokhlo

kokhlo commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current master — the requires:rebase label and the conflict are both gone (mergeable: true).

The one hunk that needed a decision was in the test file: master had added two createSpacedXAxisFormatter tests for the new showAllLabels parameter in the same region my regression test landed. I kept both — upstream's two tests and the sub-second one — so nothing upstream is lost and the fix keeps its own coverage.

Diff vs master is unchanged: formatters.ts +5/-5, formatters.test.ts +11/-0. The two commits are the same two as before, only rebased (cc5da15, 9cf2b29). formatters.test.ts suite: 27/27 green, the whole plugin-chart-echarts/test/utils/ directory 238/238.

@netlify

netlify Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 784b0fb
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6ac37d74a229ab0008615428
😎 Deploy Preview https://deploy-preview-44701--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot removed the requires:rebase Requires rebasing on top of current master label Sep 30, 2026
@bito-code-review

bito-code-review Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #8be9db

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: cc5da15..9cf2b29
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/formatters.ts
    • superset-frontend/plugins/plugin-chart-echarts/test/utils/formatters.test.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@github-actions github-actions Bot added the requires:rebase Requires rebasing on top of current master label Oct 3, 2026
kokhlo added 2 commits October 5, 2026 13:15
# Conflicts:
#	superset-frontend/plugins/plugin-chart-echarts/test/utils/formatters.test.ts
… precision

Target the boundary labels specifically instead of flooring every
no-grain tick: createPaddedExtentFloorFormatter wraps the axis
formatter and floors to the second only ticks that fall outside the
data domain (ECharts axis-extent padding), while ticks inside the
domain keep their sub-second precision. Wired in Timeseries and
MixedTimeseries via getXAxisDomain.
@pull-request-size pull-request-size Bot added size/L and removed size/S labels Oct 5, 2026
@kokhlo

kokhlo commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

You're right — flooring every no-grain tick throws away precision the series may actually carry. Reworked in 784b0fb to target the boundary label specifically, and dropped the shared-wrapper change in `getSmartDateFormatter` entirely (the no-grain path returns the raw base formatter again, as on master).

The flooring now lives in a new `createPaddedExtentFloorFormatter` wired into the Timeseries and MixedTimeseries axes: it receives the data domain from `getXAxisDomain` and floors to the second only ticks that fall outside that domain — ECharts axis-extent padding. Ticks inside the domain pass through untouched.

Verified through the real `transformProps` (Top-10-Games-shaped chart, temporal `year` column, no grain), same three ticks on each head:

tick master previous PR head 784b0fb
padded max boundary (+943ms) `.943ms` `2009` `2009`
genuine in-domain (+500ms) `.500ms` `2006` `.500ms`
clean year tick `2009` `2009` `2009`

Also rebased onto current master — the conflict with the new `coerceTemporalMs`/`getXAxisDomain` tests is resolved by keeping both sides (`mergeable: true`). Unit tests: formatters + Timeseries + MixedTimeseries suites, 518 passed.

@github-actions github-actions Bot added risk:chart-metadata and removed requires:rebase Requires rebasing on top of current master labels Oct 5, 2026

@bito-code-review bito-code-review Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review Agent Run #0fd67a

Actionable Suggestions - 1
  • superset-frontend/plugins/plugin-chart-echarts/src/utils/formatters.ts - 1
Additional Suggestions - 1
  • superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts - 1
    • Redundant domain scan · Line 765-771
      `getXAxisDomain` now runs unconditionally on every render, but its result is only consumed when `xAxisType === AxisType.Time` (via `createPaddedExtentFloorFormatter` and `showMaxLabel`). For non-Time (e.g. category) axes this adds a full O(n) scan over all records — `coerceTemporalMs` constructs a `new Date` per string value — that was previously skipped. Guard it behind the Time check.
Review Details
  • Files reviewed - 4 · Commit Range: 9cf2b29..784b0fb
    • superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/formatters.ts
    • superset-frontend/plugins/plugin-chart-echarts/test/utils/formatters.test.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread superset-frontend/plugins/plugin-chart-echarts/src/utils/formatters.ts Outdated
…le domain scan

Two review findings on the padded-extent formatter.

Every x-axis formatter factory had to copy the wrapped formatter's id
onto its wrapper, or ECharts re-renders each tick because its label
cache misses. The third copy made the contract three times over, so it
now lives in one helper.

The data-extent scan coerces every record's x value, and only the two
Time-axis formatters read its result, so it now runs behind that check
instead of on every render of a category axis.
@bito-code-review

bito-code-review Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Agent Run #db0b69

Actionable Suggestions - 0
Review Details
  • Files reviewed - 4 · Commit Range: 784b0fb..d1f8501
    • superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
    • superset-frontend/plugins/plugin-chart-echarts/src/utils/formatters.ts
    • superset-frontend/plugins/plugin-chart-echarts/test/MixedTimeseries/transformProps.test.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ECharts Temporal X-Axis Shows .XXXms Instead of Year

2 participants