From 823cef4e815f0d3c479158be601a61051842bcb6 Mon Sep 17 00:00:00 2001 From: Pedro Rossato Date: Thu, 29 Oct 2026 13:52:43 -0300 Subject: [PATCH] Read built-in number format 17 (mmm-yy) as DateTime The built-in number format 17 was declared with typeof(TimeSpan). ConvertValueByStyleFormat only handles DateTime? and TimeSpan?, so cells using it were returned as the raw OADate serial. Declare it as typeof(DateTime?), like the other built-in date formats (14, 15, 16 and 22). Fixes #1034 --- src/MiniExcel/OpenXml/ExcelOpenXmlStyles.cs | 2 +- tests/MiniExcelTests/MiniExcelIssueTests.cs | 33 +++++++++++++++++++++ tests/MiniExcelTests/Utils/Helpers.cs | 15 ++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlStyles.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlStyles.cs index fc00794d..b2062268 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlStyles.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlStyles.cs @@ -154,7 +154,7 @@ public object ConvertValueByStyleFormat(int index, object value) { 14, new NumberFormatString("d/m/yyyy",typeof(DateTime?)) }, { 15, new NumberFormatString("d-mmm-yy",typeof(DateTime?)) }, { 16, new NumberFormatString("d-mmm",typeof(DateTime?)) }, - { 17, new NumberFormatString("mmm-yy",typeof(TimeSpan)) }, + { 17, new NumberFormatString("mmm-yy",typeof(DateTime?)) }, { 18, new NumberFormatString("h:mm AM/PM",typeof(TimeSpan?)) }, { 19, new NumberFormatString("h:mm:ss AM/PM",typeof(TimeSpan?)) }, { 20, new NumberFormatString("h:mm",typeof(TimeSpan?)) }, diff --git a/tests/MiniExcelTests/MiniExcelIssueTests.cs b/tests/MiniExcelTests/MiniExcelIssueTests.cs index 1ee11af5..7124391a 100644 --- a/tests/MiniExcelTests/MiniExcelIssueTests.cs +++ b/tests/MiniExcelTests/MiniExcelIssueTests.cs @@ -4919,4 +4919,37 @@ public void TestIssue915() Assert.Equal(7.4, result[1].Altitude); Assert.Equal(8.6, result[2].Altitude); } + + // https://github.com/mini-software/MiniExcel/issues/1034 + [Theory] + [InlineData(14)] + [InlineData(15)] + [InlineData(16)] + [InlineData(17)] + [InlineData(22)] + public void QueryReadsBuiltInDateFormatsAsDateTime(int numFmtId) + { + using var path = AutoDeletingPath.Create(); + MiniExcel.SaveAs(path.ToString(), new[] { new { Value = "control" } }); + Helpers.ReplaceStylesXml(path.ToString(), $""" + + + + + + + + """); + Helpers.ReplaceFirstSheetXml(path.ToString(), """ + + + + 46327 + + """); + + var row = MiniExcel.Query(path.ToString()).Single(); + + Assert.Equal(new DateTime(2026, 11, 1), (object)row.A); + } } diff --git a/tests/MiniExcelTests/Utils/Helpers.cs b/tests/MiniExcelTests/Utils/Helpers.cs index a183493d..b0e8c1c8 100644 --- a/tests/MiniExcelTests/Utils/Helpers.cs +++ b/tests/MiniExcelTests/Utils/Helpers.cs @@ -91,6 +91,21 @@ internal static void ReplaceFirstSheetXml(string path, string worksheetXml) writer.Write(worksheetXml); } + internal static void ReplaceStylesXml(string path, string stylesXml) + { + using var stream = File.Open(path, FileMode.Open, FileAccess.ReadWrite); + using var archive = new ZipArchive(stream, ZipArchiveMode.Update, false, Encoding.UTF8); + var styles = archive.Entries.Single(w => + w.FullName.Equals("xl/styles.xml", StringComparison.OrdinalIgnoreCase) || + w.FullName.Equals("/xl/styles.xml", StringComparison.OrdinalIgnoreCase)); + var entryName = styles.FullName; + styles.Delete(); + + var replacement = archive.CreateEntry(entryName); + using var writer = new StreamWriter(replacement.Open(), new UTF8Encoding(false)); + writer.Write(stylesXml); + } + internal static string? GetFirstSheetDimensionRefValue(string path) { var ns = new XmlNamespaceManager(new NameTable());