All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.6.4] - 2026-10-14
No breaking changes: the public API, supported Elixir requirement (~> 1.18)
and all runtime mix.exs constraints are unchanged. Dependency updates only.
Added
- Legacy
.xlsfiles declaring code page 10007 (Mac Cyrillic) or 21010 (UTF-16LE) now open; previously calamine failed with "Codepage … not found" (codepage0.1.2 → 0.1.3).
Updated
- Updated Rust dependencies:
encoding_rs0.8.35 → 0.8.42: rewritten ASCII fast path, UTF-8 validation viasimdutf8, and a fix for a boundary bug in streaming legacy decoders (calamine uses the non-streaming API, so results are unchanged). Pulls in four new crates:simdutf8,scopeguard,core_detectandmultiversion_no_op.flate21.1.9 → 1.1.10: a stream truncated at end-of-file is now an error instead of a silent short read, plus an infinite-loop fix in gzip header writing. Dropsminiz_oxideandadler2from the lockfile.- flate2 1.1.10 moved zlib-rs runtime CPU feature detection (SIMD) behind a
new default feature, which
zipdisables.flate2is now listed directly inCargo.tomlwithruntime_detectionenabled, so decompression keeps using SIMD. The backend is still zlib-rs. - Transitive:
cc,cfg-if,crc32fast,equivalent,find-msvc-tools,indexmap,inventory,js-sys,libc,syn,unicode-ident,wasm-bindgen(+ macro crates),zlib-rs,zopfli. rustlerstays on 0.38.0: crate 0.39.0 is out, but the hex package is still 0.38.0 and the two must match.- Verified parsing is unaffected: every fixture in
test/files(.ods,.xls,.xlsxand both hidden-sheet variants) was parsed before and after the update viasheet_names/2,sheet_names_from_binary/2,parse/2andparse_from_binary/2, producing identical results.
- Tested against
rustler_precompiled0.10.0, which the existing~> 0.8constraint already allows (adds NIF 2.18 / OTP 29 support; the options this library passes and the checksum file format are unchanged). - Tested against
decimal3.2.0. - Updated Elixir dev/test dependencies:
ex_doc0.40.4,igniter0.8.4, and their transitives (ex_ast,finch,hpax,makeup,mint,req,sourceror,spitfire). Dropped the staleinflexandtomllock entries, which no environment resolved.
Removed
- Dropped the unused
dialyxirdev/test dependency.
Internal
- CI actions updated to their current major versions.
[0.6.3] - 2026-08-25
No breaking changes: the public API, supported Elixir requirement (~> 1.18)
and all runtime Elixir dependencies are unchanged. Dependency updates only.
Updated
- Updated transitive Rust dependencies (
android_system_properties,cc,crc32fast,fast-float2,find-msvc-tools,futures-core,futures-task,futures-util,js-sys,log,syn,wasm-bindgen,wasm-bindgen-macro,wasm-bindgen-macro-support,wasm-bindgen-shared,zlib-rs). The direct Rust dependencies (calamine0.36.1,rustler0.38.0,chrono0.4.45,serde1.0.229) were already at their latest releases and are unchanged.- Verified parsing is unaffected: every fixture in
test/files(.ods,.xls,.xlsxand both hidden-sheet variants) was parsed before and after the update viasheet_names/2,sheet_names_from_binary/2,parse/2andparse_from_binary/2, producing identical results.
- Verified parsing is unaffected: every fixture in
- Updated Elixir dev/test dependencies (no runtime deps changed):
earmark_parser1.4.45 → 1.4.46,req0.7.1 → 0.7.3,spitfire0.3.13 → 0.4.0. Dropped the stalecastorelock entry, which no environment resolved.
[0.6.2] - 2026-07-30
No breaking changes: the public API, supported Elixir requirement (~> 1.18)
and all runtime Elixir dependencies are unchanged. Dependency updates only.
Updated
- Updated Rust dependencies:
- calamine 0.36.0 → 0.36.1
- Bug fixes only: fixed a 0.36.0 regression that broke Strict OOXML
(ISO/IEC 29500)
_rels/.relslinks, fixed XLSB parsing of unhandled workbook-global records (previously could silently drop a worksheet or panic), and XLS string cells now preserve empty strings instead of discarding them.
- Bug fixes only: fixed a 0.36.0 regression that broke Strict OOXML
(ISO/IEC 29500)
- serde 1.0.228 → 1.0.229
- Updated transitive Rust dependencies (
cc,futures-core,futures-task,futures-util,libc,proc-macro2,quote,syn)
- calamine 0.36.0 → 0.36.1
- Updated Elixir dev/test dependencies (no runtime deps changed):
igniter0.8.2 → 0.8.3,ex_ast0.12.10 → 0.13.1,glob_ex0.1.11 → 0.1.12,req0.6.3 → 0.7.1
[0.6.1] - 2026-07-17
No breaking changes: the public API, supported Elixir requirement (~> 1.18)
and all runtime Elixir dependencies are unchanged.
Changed
- CI now tests every supported Elixir version (1.18, 1.19 and 1.20) against every Erlang/OTP version each of them supports (26 through 29), using the latest patch releases (1.18.4 / 1.19.5 / 1.20.2, OTP 26.2 / 27.3 / 28.5 / 29.0).
Updated
- Updated Rust dependencies:
- calamine 0.35.0 → 0.36.0
- No API changes affecting this library (verified: only
open_workbook_auto*,sheets_metadataandworksheet_rangeare used). - Bug fixes: shared-formula cell-name replacement for defined names, custom
number-format detection for the
*(fill/repeat) operator, better XLS error messages for truncated files. - Behaviour note: XLSX text cells now trim leading/trailing ASCII
whitespace unless
xml:space="preserve"is set, matching Excel and the ECMA-376 spec. Files written by Excel, LibreOffice, openpyxl etc. set this attribute when whitespace is intended, so their output is unaffected; only spec-violating writers are normalised.
- No API changes affecting this library (verified: only
- Updated transitive Rust dependencies (
atoi_simd,cc,memchr,quick-xml,syn,wasm-bindgen,zip,zlib-rs, …)
- calamine 0.35.0 → 0.36.0
- Updated Elixir dev/test dependencies (transitive only, no runtime deps
changed):
ex_ast0.12.0 → 0.12.10,hpax1.0.3 → 1.0.4,makeup1.2.1 → 1.2.2,mint1.9.0 → 1.9.3,req0.6.2 → 0.6.3
[0.6.0] - 2026-06-24
Fixed
- Breaking:
.odsdates are now normalised toNaiveDateTime(date-only values become midnight), matching the.xlsx/.xlsoutput. Previously they leaked back as raw ISO 8601 strings, so the type of a date cell depended on the source format. - Duration-formatted cells (e.g.
[h]:mm:ss) are no longer misread as bogus dates. Calamine surfaces them asDateTime/TimeDeltavalues; they are now returned as ISO 8601 duration strings (e.g."PT1H30M0S").
Changed
- Internal cleanup: deduplicated the path/binary NIF bodies behind generic Rust helpers, and the all-sheets/format-error handling in the Elixir wrapper. No user-facing behaviour change.
- Removed the inert
doctest Spreadsheetdeclaration (the@docexamples are illustrative and were never executed as doctests).
Updated
- Updated Elixir dependencies:
- rustler 0.37.3 → 0.38.0
- Added
Term::sizesupport on Erlang 29 (nif_version_2_18), direct UTF-8 atom support (nif_version_2_17),NewBinary::from_iter, anddeserialize_seqfor binary deserialization - Deprecated the previous atom-creation signatures (alongside the new UTF-8
variants) and removed some deprecated codegen features. No code changes
were required — this library uses only
rustler::nif,NifTaggedEnum, andrustler::init!, none of which are affected. Not breaking for callers.
- Added
credo1.7.18 → 1.7.19 (dev/test)decimal3.1.0 → 3.1.1ex_doc0.40.2 → 0.40.3 (dev)igniter0.8.0 → 0.8.2 (dev/test)
- rustler 0.37.3 → 0.38.0
- Updated Rust dependencies:
[0.5.0] - 2026-05-15
Added
Spreadsheet.Calamine.parse_all_from_path/2andparse_all_from_binary/2— open the workbook once and return every sheet, replacing the previous open-per-sheet loop inSpreadsheet.parse/2.
Changed
- Breaking: Excel formula errors (
#REF!,#DIV/0!,#N/A, …) are now returned as{:error, reason}tuples instead of bare strings, so they are distinguishable from text cells. - All NIFs are now scheduled on
DirtyCputo avoid stalling the BEAM scheduler when parsing large workbooks.
Fixed
- Datetimes that calamine cannot convert no longer leak the literal string
"Invalid DateTime"into the result — they becomenil. - Sub-second precision in
Data::DateTimecells is preserved (was previously truncated to whole seconds by the format string). - Hidden-sheet filtering now uses sheet metadata in both branches, removing a
latent inconsistency between
hidden: trueandhidden: false.
Updated
- Updated Elixir dependencies:
erlex0.2.8 → 0.2.9,ex_ast0.11.2 → 0.12.0 - Updated transitive Rust dependencies (
cc,hashbrown,js-sys,libc,quick-xml,wasm-bindgen)
[0.4.10] - 2026-05-12
- Relax
rustler_precompiledpackage version
[0.4.9] - 2026-05-12
Added
- Allow
decimalpackage version 2.0 or 3.0 (~> 2.0 or ~> 3.0)
Updated
- Updated
rustler_precompiled0.8.1 → 0.9.0 - Updated Rust dependencies:
- calamine 0.34.0 → 0.35.0
- Added
has_1904_epoch()method on XLS/XLSX/XLSB structs to detect Excel date epoch - Made
RowDeserializerpublic for customDeserializeSeedimplementations - Performance: optimised
<v>cell value parsing and cell reference parsing - Bug fixes: redundant
<v>in XLSX inlineStr cells, XML namespace prefixes in relation ids, rich text<r>after plain<t>, XLS date formats for formula values, XLSX shared formula expansion corrupting function names (e.g.LOG10), inlineStr CDATA handling, BIFF5 formula parse panic, panic on unsupported XLS formula functions
- Added
- rustler 0.37.3 → 0.37.4
- Fixed OTP 22/23 heap overflow by gating
ErlNifResourceTypeInitmembers/dyncall onnif_version_2_16
- Fixed OTP 22/23 heap overflow by gating
- calamine 0.34.0 → 0.35.0
[0.4.7] - 2026-03-18
Updated
- Updated Rust dependencies:
- calamine 0.33.0 → 0.34.0
- Performance improvements: cell reader buffer reuse, optimised XLS sector chain reads, cached path/attr lookups, minimised allocations
- Bug fixes: capitalized workbook/book stream handling in XLS, BIFF5 record parsing, empty
<v/>values in XLSX, millisecond rounding
- rustler 0.37.2 → 0.37.3
- calamine 0.33.0 → 0.34.0
[0.4.6] - 2026-03-02
Updated
[0.4.5] - 2026-01-20
Updated
- Updated Rust dependencies
[0.4.3] - 2025-12-05
Updated
- Updated Rust dependencies:
- rustler 0.37.0 → 0.37.2
[0.4.2] - 2025-11-24
Updated
- Updated Rust dependencies in native spreadsheet module:
- calamine 0.31.0 → 0.32.0
[0.4.1] - 2025-11-17
Changed
- Dependency updates
[0.4.0] - 2025-10-29
Added
- NEW:
parse/2now supports parsing all sheets at once when called without the:sheetoption- Returns a list of
{sheet_name, sheet_data}tuples in sheet order - Supports
:hiddenoption to exclude hidden sheets - Works with both file paths and binary content via
:formatoption - Example:
Spreadsheet.parse("workbook.xlsx")returns{:ok, [{"Sheet1", [...]}, {"Sheet2", [...]}]}
- Returns a list of
Changed
- BREAKING: Unified API interface for
sheet_names/2andparse/2functionssheet_names/2now accepts a:formatoption (:filenameor:binary) instead of having separate_from_binaryvariantparse/2now uses a:sheetoption to specify which sheet to parse, instead of a separate parameter- Default format is
:filenamefor backwards compatibility with existing code that doesn't pass options - Examples:
Spreadsheet.sheet_names(content, format: :binary)replacesSpreadsheet.sheet_names_from_binary(content)Spreadsheet.parse(content, sheet: "Sheet1", format: :binary)replacesSpreadsheet.parse_from_binary(content, "Sheet1")Spreadsheet.parse("file.xlsx", sheet: "Sheet1")replacesSpreadsheet.parse("file.xlsx", "Sheet1")
Deprecated
sheet_names_from_binary/2- Usesheet_names/2withformat: :binaryinsteadparse_from_binary/2- Useparse/2withsheet:andformat: :binaryoptions instead
Fixed
- Improved error messages for invalid format options
[0.3.0] - 2025-10-13
Updated
- Updated Rust dependencies in native spreadsheet module:
- Changed calamine feature flag from
datestochrono
[0.2.4] - 2025-09-03
Updated
- Updated calamine dependency to version 0.30.0
[0.2.3] - 2025-01-08
Updated
- Updated calamine dependency to version 0.29.0
[0.2.2] - 2025-07-23
Added
- Support for filtering hidden sheets in
sheet_names/2andsheet_names_from_binary/2functions