Repository navigation
fix(format): support thousands grouping and format sections in numberFormat/TEXT (HF-287) - #1716
marcin-kordas-hoc wants to merge 14 commits into
Conversation
✅ Deploy Preview for hyperformula-dev-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
bugbot run |
98bb963 to
8a64987
Compare
Performance comparison of head (eb272ea) vs base (d143456) |
8a64987 to
b92d6c0
Compare
…Format/TEXT (HF-287)
The TEXT number formatter understood only a single simple mask
(`[#0]+(\.[#0]*)?`), so complex masks leaked their unparsed tail into the
output (e.g. `TEXT(1234.5,"#,##0.00")` -> `1235,##0.00`) and it ignored
the instance's configured separators.
Extend the existing formatter in place (Option A):
- parser.ts: widen the number-format regex to a FLAT class `[#0,]+(\.[#0]*)?`
that admits the grouping comma (no nested quantifier — DEV-2120 ReDoS
discipline). Export its source for a white-box shape test.
- format.ts: strip presentational color tags (`[Red]`, ...) after the currency
callback and before date/time dispatch; split the mask into sign-selected
sections (positive;negative;zero) honoring quotes/escapes; thread Config so
the decimal glyph uses `decimalSeparator` and grouping uses `thousandSeparator`
(empty on default config -> no visible glyph). Sign is extracted on `abs`,
fixing the pre-existing `padLeft('-5',3)` bug (`TEXT(-5,"000.00")` -> `-005.00`).
Trailing scaler commas degrade to a visible literal rather than silently
mis-scaling. Parse failures fall back to the cleaned format string.
No public API change, no i18n, no grammar rewrite. Percent scaling, scaler
arithmetic, `?` placeholders, scientific notation and `[condition]` comparators
remain out of scope.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
b92d6c0 to
fa890f6
Compare
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit fa890f6. Configure here.
The public test/ directory holds smoke tests only; internal test suites live in the hyperformula-tests repository. The spec moves there unchanged in coverage, split into single-assertion cases: handsontable/hyperformula-tests#27. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…cks (HF-287) Color tags are removed from the format string before it reaches the stringifyDateTime and stringifyDuration callbacks, so a custom callback no longer sees them. That is client-visible and belongs in the entry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The file cites the reporting issue in 93 entries and a PR in 12, and HF-24's stringifyCurrency entry in this same release already points at #1145. That issue asked for two things — support for the $#,##0.00 format, or a stringifyCurrency config option. HF-24 shipped the alternative; this change delivers the primary ask, so the entry references the issue rather than the pull request. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
hyperformula-docs | eb272ea | Commit Preview URL Branch Preview URL |
Oct 25 2026, 01:50 AM |
This morning's develop merge landed the TEXT/numberFormat entry inside the already-published 3.4.0 Fixed list, putting CHANGELOG.md out of sync with docs/guide/release-notes.md for that released version. The fix is unreleased, so the entry belongs under [Unreleased]; the 3.4.0 lists in the two files match again. Same correction as 63e2137 on #1714. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…HF-287)
Two divergences measured against real Excel via the MS Graph workbook
API (2026-08-28):
- TEXT(-0.4,"0") rendered "-0" — the implicitly-prepended minus is now
dropped when every rendered digit rounds to zero; explicit `-` mask
literals and raw-sign section selection are unchanged.
- TEXT(1E+21,"#,##0") rendered JS scientific notation ("1e+21") — doubles
>= 1e21 are always integer-valued, so they are expanded to their exact
digits via BigInt before padding and grouping.
tsconfig lib gains es2020.bigint for the BigInt global — a types-only
change with no runtime or output impact (all supported browsers and Node
versions provide BigInt).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 975b72c. Configure here.
|
|
||
| - Fixed the MAXPOOL and MEDIANPOOL functions throwing an uncaught `TypeError` instead of returning the `#VALUE!` error when the range dimensions are not a whole multiple of the window size and the stride. [#1718](https://github.com/handsontable/hyperformula/pull/1718) | ||
| - Fixed the `MOD` function returning a remainder with the sign of the dividend instead of the sign of the divisor, which made the results differ from Excel and Google Sheets for arguments with opposite signs (e.g. `=MOD(-3, 12)` now returns `9` instead of `-3`). [#1747](https://github.com/handsontable/hyperformula/issues/1747) | ||
| - Fixed the `TEXT` function so that number-format masks with thousands grouping (`#,##0`) and positive/negative/zero sections (`0.00;(0.00)`) are formatted correctly instead of leaking the unparsed mask into the output. The built-in number formatter now also honors the configured `decimalSeparator` and `thousandSeparator` and ignores color tags such as `[Red]`, which are now removed from the format string before it reaches the `stringifyDateTime` and `stringifyDuration` callbacks. Matching Excel, a negative value whose rendered digits all round to zero is now displayed without the minus sign (`TEXT(-0.4, "0")` returns `0`), and magnitudes of `1e21` and above are expanded to their full digits instead of scientific notation. [#1145](https://github.com/handsontable/hyperformula/issues/1145) |
There was a problem hiding this comment.
CHANGELOG uses issue not PR link
Low Severity
The new Unreleased TEXT/numberFormat bullet ends with an issue URL for #1145 rather than a GitHub pull-request link. Unreleased changelog items need a pull/NNNN link; an issue-only reference does not count.
Triggered by learned rule: CHANGELOG bullets need a PR link
Reviewed by Cursor Bugbot for commit 975b72c. Configure here.
Tobiadefami
left a comment
There was a problem hiding this comment.
Looks good. Grouping respects the configured separators, and positive, negative, and zero values select the expected format sections.
… changelog entry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
…k without its color section Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1716 +/- ##
===========================================
+ Coverage 97.28% 97.29% +0.01%
===========================================
Files 207 207
Lines 16218 16289 +71
Branches 3556 3600 +44
===========================================
+ Hits 15777 15848 +71
Misses 433 433
Partials 8 8
🚀 New features to boost your workflow:
|


Context
TEXTand the built-in number formatter understood only a plain mask of digit placeholders with an optional decimal part. Other masks leaked their tail into the result:TEXT(1234.5,"#,##0.00")returned1235,##0.00. Thousands grouping (#,##0), sections (positive;negative;zero), and thedecimalSeparatorandthousandSeparatoroptions were ignored.This PR extends the formatter in place rather than rewriting its grammar:
,inside its flat character class, with no nested quantifier, so it stays free of catastrophic backtracking;thousandSeparator, and writes the decimal point withdecimalSeparator. A section it cannot parse is rendered as literal text. Values of 1e21 and above are converted to digits withBigInt, which is whyhyperformula/tsconfig.jsonadds thees2020.bigintlibrary;[Red]are stripped after thestringifyCurrencycallback and before date and time formatting. That fixes[Red]dd-mm-yyyy, whosedinRedwas read as a day token:TEXT(1,"[Red]dd-mm-yyyy")returned[Re31]31-12-1899and now returns31-12-1899. As a consequence, a customstringifyDateTimenow receives the mask without color tags (dd-mm-yyyy), whilestringifyCurrencystill receives it unchanged ([Red]0.00).The compatibility guide (
docs/guide/compatibility-with-microsoft-excel.md) now lists what the built-in formatter handles and what still needs a callback.How did you test your changes?
Paired tests: handsontable/hyperformula-tests#27, branch
feat/hf-287-numberformat-sections.unit/format/format-number-sections.spec.ts: 65 tests, all green on this head. Run againstdevelop, with its one white-box import stubbed (the exported regular-expression source does not exist there), 43 of them fail.unit/interpreter/function-text.spec.ts: one existing assertion changes,TEXT(12.45,"$###,##0.00")from$12,##0.00to$12.45on the default config.Results that changed for masks that used to work: on the default config,
TEXT(5,"0,000")was5,000and is now0005, because the defaultthousandSeparatoris empty and the old output was the mask tail, not grouping.Compatibility with Excel and Google Sheets
HyperFormula results measured on this head, with
thousandSeparator: ','andfunctionArgSeparator: ';'unless noted. The Excel column is the expected result of each mask and was not re-measured for this revision. Google Sheets was not checked.=TEXT(1234.5;"#,##0.00")1,234.501,234.50=TEXT(-1234.5;"#,##0.00;-#,##0.00")-1,234.50-1,234.50=TEXT(-1234.5;"$#,##0.00;-$#,##0.00")-$1,234.50-$1,234.50=TEXT(1234.567;"$#,##0.00")$1,234.57$1,234.57=TEXT(-5,"000.00")(default config)-005.00-005.00The formatter still does not implement percent scaling (
0.00%returns0.50%for 0.5), scaler commas (0,,), the?placeholder, scientific notation (0.00E+00) or[condition]comparators; the compatibility guide lists them.Public API changes
None in signatures. Behaviour: a custom
stringifyDateTimecallback receives the format without color tags, as described in Context.Out of scope and follow-ups
Types of changes
Related issues
TEXT()function should support popular currency formats #1145, which asked for$#,##0.00inTEXT.Checklist
dev-docs/pages this change affects – or none needed it.Note
Medium Risk
Changes core TEXT/formatting behavior and what custom stringifyDateTime receives; some previously wrong outputs (e.g. masks that looked like grouping with default config) will differ intentionally.
Overview
Fixes
TEXTand the sharedformat()number path so Excel-style masks like#,##0and multi-section patterns (0.00;(0.00)) render correctly instead of leaking mask tail text (e.g.1235,##0.00). The formatter now splits sections on unescaped;(respecting quotes/escapes), picks positive/negative/zero by value sign, appliesdecimalSeparator/thousandSeparatorfrom config, handles literal-only sections, signed-zero rounding, and usesBigIntfor magnitudes ≥ 1e21 (es2020.bigintin tsconfig).Color tags (
[Red], etc.) are stripped afterstringifyCurrencyand before date/duration dispatch, so colored date masks format as dates and customstringifyDateTimereceives the mask without the color segment (documented as a behavior change). The number-format tokenizer admits grouping commas with a flat regex and only treats runs containing#/0as number tokens.Docs and CHANGELOG cover supported vs callback-only format features and the
stringifyDateTimecallback input change.Reviewed by Cursor Bugbot for commit eb272ea. Bugbot is set up for automated code reviews on this repo. Configure here.