Skip to content

Keep the 1904 date system when reading and writing - #375

Merged
MathNya merged 2 commits into
MathNya:masterfrom
keithadler:fix-date1904-roundtrip
Oct 1, 2026
Merged

MathNya merged 2 commits into
MathNya:masterfrom
keithadler:fix-date1904-roundtrip

Conversation

@keithadler

Copy link
Copy Markdown
Contributor

Problem

workbookPr/@date1904 is never read, and the writer always emits <workbookPr filterPrivacy="1"/>. So opening a workbook that uses the 1904 date system and saving it turns it into a 1900-system workbook, and every date in it moves back by 1,462 days (4 years and a day), with no error:

workbookPr serial 45000 in Excel / openpyxl
original file <workbookPr date1904="1"/> 2027-03-16
after reader::xlsx::read + writer::xlsx::write <workbookPr filterPrivacy="1"/> 2023-03-15

Workbooks from older Mac versions of Excel use the 1904 system.

Change

  • Read date1904 from workbookPr (both "1" and "true", since it is an xsd:boolean).
  • Write it back when set.
  • Expose it as Workbook::date1904() / Workbook::set_date1904().

Cell::formatted_value() still formats serials in the 1900 system, since a cell has no access to its workbook. Fixing that needs an API decision (for example passing the date system into the formatter), so I left it out of this PR. With this change a caller can at least read date1904() and adjust.

Tests

tests/date1904_roundtrip.rs: the default is false and not written; true survives write → read → write; the "true" spelling is read. cargo +nightly fmt --all --check, cargo clippy -- -D warnings and cargo test pass.

Found by differential testing with xlsx-lean, a formal model of the XLSX format in Lean, whose corpus includes a 1904-system workbook.

🤖 Generated with Claude Code

https://claude.ai/code/session_019hXzkiuCwk5QQRsyU9wT9h

`workbookPr/@date1904` was never read, and the writer always emitted
`<workbookPr filterPrivacy="1"/>`. Opening a 1904-system workbook and
saving it therefore turned it into a 1900-system workbook, which moves
every date in it back by 1,462 days (4 years and a day): serial 45000
reads as 2027-03-16 before the round trip and 2023-03-15 after it.

Read the attribute (both "1" and "true" spellings of xsd:boolean),
write it back when set, and expose it as `Workbook::date1904()` /
`Workbook::set_date1904()`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hXzkiuCwk5QQRsyU9wT9h
Comment thread src/structs/workbook.rs Outdated

/// Whether the workbook uses the 1904 date system (`workbookPr/@date1904`).
///
/// In the 1904 system, serial 0 is 1904-01-01 instead of 1900-01-00, so the

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.

Don't have the time to double check right now but this seems wrong. I think serial 0 is 1899-12-31 and serial 1 is 1900-01-01.

Just read the comment over and yeah the comment is wrong for sure (even though I'm not 100% on the above) because 1900-01-00 is NOT a real date.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, good catch. Excel does display serial 0 as 1900-01-00, but you're right that it isn't a real date and doesn't belong in a doc comment. I've reworded it in fe6f031: serial 0 is 1904-01-01 and serial 1 is 1904-01-02 in the 1904 system, serial 1 is 1900-01-01 in the 1900 system, and the 1,462-day offset is stated as holding from 1900-03-01 on (past Excel's fictitious 1900-02-29). Happy to help with anything else here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MathNya

MathNya commented Oct 1, 2026

Copy link
Copy Markdown
Owner

@keithadler
Since there are no issues here either, I will go ahead and merge it.

@c-git
Thank you as always.

@MathNya
MathNya merged commit 1bcf4d0 into MathNya:master Oct 1, 2026
5 checks passed
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.

4 participants