Repository navigation
Read and write dates of a date1904 workbook on the 1904 epoch - #12
Conversation
An .xlsx workbook may count its date serials from 1 January 1904 (<workbookPr date1904="1"/>), 1,462 days behind the default epoch; the .xls reader already honoured the equivalent DATEMODE record, but the .xlsx path converted every serial from the 1900 epoch, so serial 1 read as 1899-12-31 instead of 1904-01-02, in both the eager and the streaming reader. The flag is now read off the workbook part, serials of a date-formatted cell are moved onto the 1900 epoch before the shared conversion, and a date written into such a workbook is moved back, so an unchanged save keeps the stored serial and a new date lands on the epoch the file declares. Formula evaluation keeps using the 1900 epoch for its serials, as before; that is a separate change in the formula engine. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The formula engine converted dates with the 1900 epoch whatever the workbook declared. In a date1904 workbook DATE(2004,1,1) returned 37987 where Excel shows 36525, YEAR(36525) read 2005, a date cell entered a formula 1,462 days late, TEXT rendered the wrong day, and recalculate() cached those results into the file. The epoch now lives on the evaluation context: cell dates, DATE/TODAY/NOW, the component and workday functions and TEXT all count from the workbook's epoch. The display path (NumFormat.format on a DateTime) is epoch-neutral and unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
All six merged and released in 2.29.0, about three hours after you opened them. I confirmed each one on main before merging rather than taking the write-ups on trust, and every claim held:
Two things worth saying back. #11 was my own regression. I taught the eager reader about prefixed parts in 2.28.0 this morning and left #7 is the one I am most glad to have. An allocation an attacker controls, reachable from The changelog credits you by name on all six. Thank you, genuinely. If you want anything else looked at, open it and I will get to it. |
Problem. A workbook may count date serials from 1 January 1904
(
<workbookPr date1904="1"/>); its serials differ by 1,462 days from thedefault date system. The
.xlsreader already honours the equivalentDATEMODErecord, but the.xlsxpath converted every serial from the 1900 epoch, so serial 1 read as1899-12-31 instead of 1904-01-02 in both the eager and the streaming reader,
and a date written through the API into such a workbook landed 1,462 days late.
Inherited from the original package. An unchanged save already kept the stored
serial and the flag, so existing dates were not corrupted on save; the parsed
values were wrong.
Fix. The flag is read off the workbook part into the model; serials of
date-formatted cells are moved onto the 1900 epoch before the shared
conversion on read, and a date is moved back onto the file's epoch on write.
Public
NumFormat.read/writeDate/writeDateTimekeep their 1900-epochmeaning. Time-formatted serials ≥ 1 also become date values in the existing
model, so they receive the same read-side shift; otherwise saving
1.5would subtract an unmatched offset and produce
-1460.5. Time-onlyfractions below one retain their existing behavior.
A second commit does the same for formula evaluation, which converted dates
with the 1900 epoch whatever the workbook declared: in a 1904 workbook
DATE(2004,1,1) returned 37987 where Excel shows 36525, a date cell entered a
formula 1,462 days late, and
recalculate()cached those serials into thefile. The serial conversions moved from top-level functions onto the
evaluation context, which takes its epoch from the workbook, so cell dates,
DATE/TODAY/NOW, the component and workday functions and TEXT all count from
the file's epoch. The display path (
NumFormat.formaton aDateTime) isepoch-neutral and unchanged.
Validation.
test/num_format_test.dart, "Xlsx Date Epoch": serial 1reads as 1904-01-02 (fails before, passes after), serials 0 / 1.5 / 36525 on
the 1904 epoch, the streamed value, an unchanged save keeping flag and serial,
a date written into a 1904 workbook saving as serial 2, a modern 1900 date unchanged, and built-in time/elapsed formats retaining
their serials, formula values and time-only fractions. Cross-checked with openpyxl on the saved files (
wb.epoch1904,A1 1904-01-02, written A2 2004-01-01).
test/formula_datetime_test.dart,"Date Serials In A 1904 Workbook": DATE/YEAR, a date cell in arithmetic,
TEXT, and a recalculated cached serial; all four fail on the previous
library and pass with the change. A real Excel-authored 1904 reference
workbook from XlsxWriter also reads all six expected dates and preserves
its serials on save. LibreOffice opens and re-saves both prepared 1904
workbooks, with dates checked afterward using openpyxl. Suite and analyzer clean.
🤖 Generated with Claude Code