Skip to content

feat(ooxml): write a date and a time into an xlsx cell - #978

Merged
andiwand merged 2 commits into
mainfrom
feat/xlsx-write-dates
Oct 4, 2026
Merged

andiwand merged 2 commits into
mainfrom
feat/xlsx-write-dates

Conversation

@andiwand

@andiwand andiwand commented Oct 4, 2026 •

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Step 3 of the typed-dates plan (decision 18, the xlsx half).

Change

  • sheet_set_cell: it takes a date or time that states its number. One without a number is refused before the cell changes.

    • A date writes serial_from_days(days, epoch), so a 1904 workbook gets its own serial.
    • A time writes its length in days as it is, because a time is a duration that no epoch moves.
  • The format: a cell whose format is not a date or time format gets one of ECMA-376 18.8.30, as Excel gives one when a date is typed:

    • 14 for a date;
    • 22 for a date with a time;
    • 20 for a time;
    • 21 for a time with seconds.

    A cell that already has a date format keeps it.

  • StyleRegistry::create_cell_format: it takes an optional numFmtId and sets applyNumberFormat. restyle_cell passes it through.

Review after the rebase

A written time of a day or more got built-in 20, h:mm, which wraps at a day, so 42:00 showed as 18:00. It gets built-in 46, [h]:mm:ss, now, as Excel gives it, and the test writes 1.75 and reads 42:00:00. The rest holds: a time is written as it is and a date as the serial of the workbook's epoch, a cell that has a date format keeps it, and create_cell_format interns the xf with its numFmtId.

Proof

  • OoxmlSpreadsheetValue.a_written_date_gets_a_date_format: a date, a date with a time, a time, and a time with seconds into plain cells. Read back after a save, they show 01-01-25, 1/1/25 18:00, 18:00 and 18:00:05, with the date and time value types.
  • OoxmlSpreadsheetValue.a_date_keeps_the_date_format_its_cell_has: in a 1904 workbook, B1 keeps s="2" and gets the serial 44197.
  • OoxmlSpreadsheetValue.*, OdfSheet* and NumberFormat.*: 85 passed.
  • LibreOffice oracle: back_translate wrote dates and times into an xlsx: into cells with dd.mm.yyyy, hh:mm and yyyy-mm-dd hh:mm, into a plain cell, and into two new cells. LibreOffice's csv export reads every one as written: 02.01.2025,18:30,2025-01-02 18:00,01/02/2025,18:00,01/02/25 06:00 PM. The last three come from the formats 14, 20 and 22.
  • g++-15 -Wall -Wextra -Werror is clean on the touched files.

@andiwand
andiwand force-pushed the feat/ods-write-dates branch from ee25c2b to 3c8286b Compare October 4, 2026 11:13
Base automatically changed from feat/ods-write-dates to main October 4, 2026 11:17
andiwand and others added 2 commits October 4, 2026 13:18
`sheet_set_cell` takes a date or time `CellValue` that states its number. A
date writes its serial in the workbook's date system, and a time writes its
length in days, which no epoch moves. A cell whose format is not a date or
time format gets one of ECMA-376 18.8.30, as Excel gives one when a date is
typed: 14 for a date, 22 for a date with a time, 20 for a time, 21 for one
with seconds. `create_cell_format` takes the number format id for that.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qFgpigFCwbfZ24TFj7eqA
Built-in 20, `h:mm`, wraps at a day, so 42:00 showed as 18:00. Excel gives
such a time built-in 46, `[h]:mm:ss`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011qFgpigFCwbfZ24TFj7eqA
@andiwand
andiwand force-pushed the feat/xlsx-write-dates branch from 46a0a3b to 036a5f4 Compare October 4, 2026 11:20
@andiwand
andiwand merged commit 18eb83b into main Oct 4, 2026
16 of 23 checks passed
@andiwand
andiwand deleted the feat/xlsx-write-dates branch October 4, 2026 11:29
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.

1 participant