You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

.NET Core项目使用OpenXML读取Excel日期格式识别问题

解决OpenXML读取Excel日期时无法识别的问题

这个问题我之前也遇到过,OpenXML处理Excel日期确实有点坑——因为Excel里的日期本质上是存成浮点数的,只有当单元格应用了日期格式时,才会显示成我们熟悉的日期样式,但OpenXML并不会直接把这种单元格标记为日期类型(也就是你遇到的Cell.DataType为null的情况)。下面给你一套可行的解决方案:

核心思路

要判断单元格是否为日期,不能只看Cell.DataType,而是要通过单元格的格式信息来判断:

  1. 从Workbook的样式部分获取单元格对应的格式(CellFormat
  2. 检查格式的NumberFormatId是否属于预定义的日期/时间格式范围
  3. 对于自定义格式,检查格式代码中是否包含日期时间相关的标识(如yyyymmdd等)
  4. 如果确认是日期格式,将单元格的数字值转换为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

火山引擎 最新活动