Skip to content

Show zero and negative date serials as Excel does (#219) - #239

Merged
SuruchBoss merged 5 commits into
mainfrom
claude/new-session-m6mehm-219
Oct 5, 2026
Merged

SuruchBoss merged 5 commits into
mainfrom
claude/new-session-m6mehm-219

Conversation

@SuruchBoss

@SuruchBoss SuruchBoss commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

What this changes

Closes #219. #225 did its [h]/[m]/[s] part; this is the rest, zero and negative serials. Next in the sprint-final queue: #59, the last one.

The bug: a value of 0 or below, under a date or time format, showed a date Excel never shows. 0 under dd/mm/yyyy read 31/12/1899, and a negative one read an invented day before it. The same was true of the grid and of TEXT, since both go through formatSerial.

Checked in Microsoft Excel by the owner (desktop, 2026-10-05), so the tests pin what Excel shows rather than a guess. The main column was probed on main's code:

Excel main this PR
A cell holding 0, format dd/mm/yyyy 00/01/1900 31/12/1899 00/01/1900
A cell holding -1, format dd/mm/yyyy ######## 30/12/1899 ########
A cell holding -1.5, format [h]:mm ######## -36:00 ########
=TEXT(0,"dd/mm/yyyy") 00/01/1900 31/12/1899 00/01/1900
=TEXT(-1,"dd/mm/yyyy") #VALUE! 30/12/1899 #VALUE!
=TEXT(-0.5,"hh:mm") #VALUE! 12:00 #VALUE!
=TEXT(-1,"[h]:mm") #VALUE! -24:00 #VALUE!

The fix:

  • formatSerial shows serial 0 as day 0 of January 1900. Only the display changes. partsOfSerial still answers 31 December 1899, because the date functions and the ISO text a cell keeps (isoFromSerial) have no day 0 to write; 1900-01-00 would not read back.
  • A negative serial is no date (showableAsDate). In the grid, formatNumberForDisplay shows ######## for it, as Excel fills the cell at its default width. TEXT gives #VALUE!.
  • A negative number under a number code is untouched: TEXT(-1,"0.0") is still -1.0.

How it was verified

Gate Result
lint · check:readme · check:screens pass
check:deps every advisory fixed or accounted for
test 2024 passed (2013 by the README's static count)
check:mutants 32/32 killed
build · check:bundle 3804/4200 KB, largest chunk 934/1300 KB
check:a11y 121 checks passed
check:e2e 60 flows passed
  • excelDate.test.ts (+2):
    • 0 as 00/01/1900 in the grid;
    • -1 under dd/mm/yyyy and -1.5 under [h]:mm as ########.
  • functions.test.ts (+1):
    • the four TEXT cases from the owner's check;
    • a negative number under a number code staying a number.
  • Re-checked against the old code: with the previous excelDate.ts, cellFormat.ts and functions.ts, all three new tests fail.
  • No guessed values are pinned. A test that would have pinned partsOfSerial(0) was taken out, because Excel's DAY(0) was not checked.
  • READMEs, both languages:
    • the "elapsed time" paragraph gains zero and negatives;
    • the excelDate.test.ts row (17 → 19).
  • Recounted: 2013 tests in 145 files, after merging [area:live-data] Sorting a range that includes a live block: the next refresh puts the live columns back, and the columns typed beside them end up on the wrong rows #235. Picture 27 retaken in both languages and checked by eye, blessed after the retake. Social card rebuilt and checked by eye.

For PO

  • The date functions still read serial 0 as 31 December 1899. DAY(0), MONTH(0) and YEAR(0) give 31, 12, 1899. What Microsoft Excel gives for them has not been checked. This PR keeps it out of scope, so nothing changes there. It would be a small ticket of its own after the pause.
  • ######## is fixed at eight characters, the width Excel shows at its default column width. Excel fills whatever width the column has; the grid shows the same eight in a wider column.

The branch

Head: claude/new-session-m6mehm-219, a second branch the owner allowed so #219 and #59 could be open together. It was opened stacked on PR #238; now that #238 is in main, its base is main. The PR for #59 is stacked on this one and merges after it.

🤖 Generated with Claude Code

https://claude.ai/code/session_013pmWZsbmugfpqtYazrzfjj

claude added 4 commits October 5, 2026 02:13
…219)

Checked in real Excel: 0 under dd/mm/yyyy shows 00/01/1900, a negative
value under a date or [h]:mm format fills the cell with #, and TEXT of a
negative value under a date or time code gives #VALUE!. The app showed
31/12/1899 and 30/12/1899, dates Excel never shows.

formatSerial now shows serial 0 as day 0 of January 1900, leaving
partsOfSerial as it is for the date functions and the ISO text a cell
keeps. A negative serial under a date or time format shows ######## in
the grid and #VALUE! from TEXT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013pmWZsbmugfpqtYazrzfjj
# Conflicts:
#	README.en.md
#	README.md
#	src/app/opengraph-image.tsx
#	src/i18n/en.ts
#	src/i18n/th.ts
Only the test counts conflicted in the two READMEs. Recounted to 2013
tests in 145 files; picture 27 retaken in both languages and the social
card rebuilt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013pmWZsbmugfpqtYazrzfjj
@vercel

vercel Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
excel-to-go Ready Ready Preview Oct 5, 2026 4:16am UTC

@SuruchBoss
SuruchBoss changed the base branch from claude/new-session-m6mehm to main October 5, 2026 04:16

Copy link
Copy Markdown
Owner Author

For PO: CodeQL (Analyze) has not run on this PR.

The other checks are all green on b4566f9. codeql.yml only runs for pull requests whose base is main, and the last push came while this PR was still stacked on #238's branch. Switching the base to main afterwards does not start it, and I won't push an empty commit to trigger it.

Two ways it gets covered:

  • the next real push to this branch (for example, a merge of main if it moves) runs it here;
  • after the merge, CodeQL's push: main trigger scans the result anyway.

If you want it on the PR before merging, say so and I'll bring in main the moment it has anything new.


Generated by Claude Code

@SuruchBoss
SuruchBoss merged commit 7c2bb7f into main Oct 5, 2026
16 checks passed

Copy link
Copy Markdown
Owner Author

PO review: merged at b4566f9, merge commit 7c2bb7f.

  • npm run verify, run locally on b4566f9: all ten gates green. 2024 tests passed, mutants 32/32, a11y 121 checks, e2e 60 flows.
  • CI: 15 checks green. CodeQL did not run on this head, as the PR says; it scans main after the merge.
  • Tried as a user on a production build, at 1280 and 390:
    • TEXT(0,"dd/mm/yyyy") is 00/01/1900, and TEXT of a negative under dd/mm/yyyy, hh:mm and [h]:mm is #VALUE!. These match the owner's Excel check.
    • TEXT(-1,"0.0") is -1.0, and TEXT(1.5,"[h]:mm") is still 36:00.
    • A formula giving −1 under dd/mm/yyyy shows ########.
    • Typing 5 into a date cell shows 05/01/1900.
    • 0.5 under dd/mm/yyyy hh:mm shows 00/01/1900 12:00.
    • Undo, then typing after it, works.
    • No page errors or alerts.

Not this PR's, but it is the way most people will meet these values: opening an .xlsx turns a 0 or negative date/time cell into text such as 1899-12-31. rawForDate in excelIO.ts writes a year-1899 ISO string that dateLiteral refuses. SUM then skips those cells in silence, and export writes the text back. It is on main already, since excelIO.ts is untouched here. Filed as #241 (sev:high, sprint-final).

Small and pre-existing, and not filed: dddd of serial 0 will show Sunday where Excel shows Saturday. Weekdays before 1 March 1900 are off by Excel's 1900 leap-day bug on main already.


Generated by Claude Code

This branch was successfully deployed

1 active deployment
Preview — b4566f93 Deployed Oct 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants