.NET Core项目使用OpenXML读取Excel日期格式识别问题
解决OpenXML读取Excel日期时无法识别的问题
这个问题我之前也遇到过,OpenXML处理Excel日期确实有点坑——因为Excel里的日期本质上是存成浮点数的,只有当单元格应用了日期格式时,才会显示成我们熟悉的日期样式,但OpenXML并不会直接把这种单元格标记为日期类型(也就是你遇到的Cell.DataType为null的情况)。下面给你一套可行的解决方案:
核心思路
要判断单元格是否为日期,不能只看Cell.DataType,而是要通过单元格的格式信息来判断:
- 从Workbook的样式部分获取单元格对应的格式(
CellFormat) - 检查格式的
NumberFormatId是否属于预定义的日期/时间格式范围 - 对于自定义格式,检查格式代码中是否包含日期时间相关的标识(如
yyyy、mm、dd等) - 如果确认是日期格式,将单元格的数字值转换为
DateTime
具体实现代码
1. 先获取样式表对象
首先需要从WorkbookPart中读取样式信息,这是判断格式的基础:
var workbookPart = document.WorkbookPart; // 你的SpreadsheetDocument对象 var stylesPart = workbookPart.WorkbookStylesPart; var stylesheet = stylesPart.Stylesheet;
2. 编写日期格式判断方法
这个方法会根据单元格的格式信息,判断是否为日期/时间格式:
private bool IsDateFormat(CellFormat cellFormat, Stylesheet stylesheet) { if (cellFormat.NumberFormatId == null) return false; uint numberFormatId = cellFormat.NumberFormatId.Value; // Excel预定义的日期/时间格式ID范围 HashSet<uint> predefinedDateFormatIds = new HashSet<uint> { 14,15,16,17,18,19,20,21,22,27,28,29,30,31,32,33,34,35,36, 45,46,47,50,51,52,53,54,55,56,57,58 }; // 先检查是否是预定义的日期格式 if (predefinedDateFormatIds.Contains(numberFormatId)) return true; // 再检查自定义格式是否包含日期时间标识 var customNumberFormat = stylesheet.NumberFormats? .Cast<NumberFormat>() .FirstOrDefault(nf => nf.NumberFormatId == numberFormatId); if (customNumberFormat != null && !string.IsNullOrEmpty(customNumberFormat.FormatCode)) { string formatCode = customNumberFormat.FormatCode.ToLowerInvariant(); return formatCode.Contains("yyyy") || formatCode.Contains("mm") || formatCode.Contains("dd") || formatCode.Contains("hh") || formatCode.Contains("ss"); } return false; }
3. 读取单元格值时判断并转换日期
在你原有的类型判断逻辑中,加入日期判断的分支:
// 假设你已经获取到当前Cell对象 string cellText = cell.CellValue?.Text; if (string.IsNullOrEmpty(cellText)) { // 处理空值逻辑 return null; } // 获取单元格对应的格式 CellFormat cellFormat = null; if (cell.StyleIndex != null) { cellFormat = stylesheet.CellFormats.ElementAt((int)cell.StyleIndex.Value); } // 判断是否为日期格式并转换 if (cellFormat != null && IsDateFormat(cellFormat, stylesheet)) { if (double.TryParse(cellText, out double excelDateValue)) { // 使用.NET自带的FromOADate方法,自动处理Excel的1900闰年bug DateTime dateTime = DateTime.FromOADate(excelDateValue); return dateTime; // 这里可以根据需求转成字符串或直接存DateTime到MongoDB } } // 以下是你原有的类型判断逻辑(decimal、boolean、string) if (bool.TryParse(cellText, out bool boolValue)) { return boolValue; } else if (decimal.TryParse(cellText, out decimal decimalValue)) { return decimalValue; } else { return cellText; }
关键注意事项
- Excel的1900闰年bug:Excel错误地将1900年视为闰年,所以会有一个不存在的
1900-02-29日期。DateTime.FromOADate方法已经自动处理了这个问题,不需要额外手动调整。 - 自定义日期格式:有些用户会用自定义格式(比如
yyyy年mm月dd日),这时候需要通过格式代码中的日期标识来判断,上面的方法已经覆盖了这种情况。 - 文本格式的日期:如果单元格是文本格式但内容是日期字符串(比如
2024-05-20),上述方法不会识别,你可以在字符串判断分支里额外加入日期解析逻辑(比如用DateTime.TryParse)。
内容的提问来源于stack exchange,提问作者Sudip




