Repository navigation
A date in a spreadsheet is read as the date it shows, not as Excel's day count - #1017
Merged
WaylandYang merged 1 commit intoOct 1, 2026
Merged
Conversation
…day count Signed-off-by: dada-yan <BinjunYann@gmail.com>
WaylandYang
approved these changes
Oct 1, 2026
WaylandYang
left a comment
Contributor
There was a problem hiding this comment.
Approving. Thanks @Maya-Kid. Reviewed at 6a0e99f: excel_date leans on calamine's own calendar (1904 epoch and the 1900 leap day included), writes a whole day as a date, a fraction as a time of day, a duration as elapsed time, and leaves anything outside Excel's calendar alone; rows classify as before since an ISO date is still a figure to table::is_numeric. The fixture is built the way Excel writes it, with the Chinese custom format among the styles.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A spreadsheet stores a date as a number: the days since 1899-12-30, with the time of day as the fraction. It is a date only because the cell's number format says so. calamine reads that format and hands such a cell over as
Data::DateTime, butparsers::spreadsheetwrites every cell withto_string(), andExcelDateTime'sDisplayprints the stored number. So every date in an.xlsxor.xlsfile reaches the base as its day count.Problem
A workbook written by openpyxl, read on dev with
cargo run -p utopia-ingest --example render_doc:In the workbook those cells show 2024-01-15, 2024年12月31日, 2024-03-01 9:30:00, 8:00:00 and 36:15:00.
.odsfiles are not affected, because calamine returns their dates as ISO strings.Change
parsers::spreadsheetwrites aData::DateTimecell with a newexcel_date:2024-01-15;2024-03-01 09:30, with seconds only when there are some;08:00, which is what anh:mmformat shows;[h]:mm:ssformats, as elapsed time,36:15:00;Details:
to_ymd_hms_milli. That handles the 1904 epoch and Excel's 1900 leap day. No dependency or feature changes.table::is_numeric. So rows are classified exactly as before, and only the cell text changes.docs/design/sources.mdsays it in the tables paragraph.Verification
Run on macOS (arm64): Rust 1.98.1; the workspace suite against PostgreSQL 16 with pgvector 0.8.2, with Poppler 26.06.
crates/utopia-ingest/tests/spreadsheet_dates.rsbuilds an.xlsxthe way Excel writes one. Dates are stored as serials understyles.xmlformats: 14 (short date), a customyyyy"年"m"月"d"日", 22 (date and time), 20 (time), 46 (elapsed time) and 4 (a plain number format).a_date_cell_reads_as_the_date_it_shows:| Short date | 2024-01-15 | 1200 |and| Chinese date | 2024-01-16 | 800 |. A General-format 45306 stays45306.a_time_of_day_and_an_elapsed_time_read_as_clock_time:2024-01-15 09:30,09:30,36:15:00.a_workbook_counting_from_1904_reads_the_same_date: 43844 in adate1904workbook reads2024-01-15.45306,45307,45306.395833333336,0.3958333333333333,1.5104166666666667and43844.2024-01-15,2024-12-31,2024-02-29and2025-02-28;2024-03-01 09:30and2024-04-18 14:05;08:00and20:30;36:15:00and0:45:00.cargo test --locked -p utopia-ingest: 96 unit tests and every integration test pass.cargo fmt --all --checkandcargo clippy --locked --workspace --all-targets -- -D warningsare clean.UTOPIA_TEST_REQUIRE_DB=1 UTOPIA_TEST_REQUIRE_PDFTOTEXT=1 cargo test --locked --workspacepasses: 1,331 passed, 0 failed, 6 ignored.utopia-ingestfixes opened alongside it: slide line breaks, Word text boxes and Word headings. Nothing outsideutopia-ingestreads parsed Office text.Limits
yyyy-mm) is written to the day:2024-01-01.yyyy"年"m"月"d"日"are read.