【问题标题】:OpenXML - Writing a date into Excel spreadsheet results in unreadable contentOpenXML - 将日期写入 Excel 电子表格会导致内容不可读
【发布时间】:2011-08-17 08:21:37
【问题描述】:

我正在使用以下代码将DateTime 添加到电子表格的列中:

var dt = DateTime.Now;
r.AppendChild<Cell>(new Cell()
    { 
        CellValue = new CellValue(dt.ToOADate().ToString()),
        DataType = new EnumValue<CellValues>(CellValues.Date), 
        StyleIndex = 1,
        CellReference = header[6] + index
    });

当我尝试在 Excel 2010 中打开文件时,出现错误

Excel 在 file.xlsx 中发现不可读的内容

如果我注释掉该行,一切都很好。

我在 StackOverflow 上提到过similar questions,但他们的代码基本上和我一样。

【问题讨论】:

  • 对此答案的评论 -- stackoverflow.com/a/528743/41153 -- 表示添加字符串时会发生这种情况。我遇到了相同的结果(字符串插入时出现“不可读的内容”)。
  • omg 谢谢你的评论,这个字符串任意搞砸了我的日期格式
  • 对于那些在 FONT、FILL 和 BORDER 下面的答案中错过这个小细节的人必须填写您的格式才能正常工作。
  • @Vince 你的评论救了我的命——传奇。在这上面浪费了这么多时间......

标签: c# .net openxml


【解决方案1】:

像往常一样迟到,但我必须发布一个答案,因为之前的所有答案都是完全错误的,除了 Oleh 被否决的答案,遗憾的是不完整。

由于问题与 Excel 有关,最简单的做法是创建一个包含所需数据和样式的 Excel 电子表格,然后将其作为部分打开并查看原始 XML。

将日期 01/01/2015 添加到单元格 A1 中会产生以下结果:

<row r="1">
  <c r="A1" s="0">
    <v>42005</v>
  </c>
</row>

请注意,type 属性在此处不是。但是,有一个样式属性引用了以下样式:

<xf numFmtId="14" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" />

这是您必须添加的最基本样式。

所以生成上面的代码:

  1. 您需要按如下方式创建样式:
var CellFormats = new CellFormats();
CellFormats.Append(new CellFormat()
{
    BorderId = 0,
    FillId = 0,
    FontId = 0,
    NumberFormatId = 14,
    FormatId = 0,
    ApplyNumberFormat = true
});
CellFormats.Count = (uint)CellFormats.ChildElements.Count;
var StyleSheet = new Stylesheet();
StyleSheet.Append(CellFormats);

NumberFormatId = 14指的是内置格式mm-dd-yy,这里是list of some other formats

不幸的是,添加 just 上述样式似乎还不够,如果这样做实际上会导致 Excel 崩溃。注意BorderIdFillIdFontId需要对应样式表中的一个项目,这意味着你需要提供它们。完整代码清单中的 GetStyleSheet() 方法提供了 Excel 正常工作所需的最低默认样式表。

  1. 并添加如下单元格:
SheetData.AppendChild(new Row(
    new Cell() 
    { 
        // CellValue is set to OADate because that's what Excel expects.
        CellValue = new CellValue(date.ToOADate().ToString(CultureInfo.InvariantCulture)), 
        // Style index set to style (0 based).
        StyleIndex = 0
    }));

注意:Office 2010 和 2013 可以以不同的方式处理日期,但默认情况下似乎不会。

它们支持 ISO 8601 格式的日期,即yyyy-MM-ddTHH:mm:ss,恰好这也是可排序的标准格式(“s”),因此您可以这样做:

SheetData.AppendChild(new Row(
    new Cell() 
    { 
        CellValue = new CellValue(date.ToString("s")), 
        // This time we do add the DataType attribute but ONLY for Office 2010+.
        DataType = CellValues.Date
        StyleIndex = 1
    }));

结果:

<row>
  <c s="0" t="d">
    <v>2015-08-05T11:13:57</v>
  </c>
</row>

完整的代码清单

以下是添加日期格式单元格所需的最少代码示例。

private static void TestExcel()
{
    using (var Spreadsheet = SpreadsheetDocument.Create("C:\\Example.xlsx", SpreadsheetDocumentType.Workbook))
    {
        // Create workbook.
        var WorkbookPart = Spreadsheet.AddWorkbookPart();
        var Workbook = WorkbookPart.Workbook = new Workbook();

        // Add Stylesheet.
        var WorkbookStylesPart = WorkbookPart.AddNewPart<WorkbookStylesPart>();
        WorkbookStylesPart.Stylesheet = GetStylesheet();
        WorkbookStylesPart.Stylesheet.Save();

        // Create worksheet.
        var WorksheetPart = Spreadsheet.WorkbookPart.AddNewPart<WorksheetPart>();
        var Worksheet = WorksheetPart.Worksheet = new Worksheet();

        // Add data to worksheet.
        var SheetData = Worksheet.AppendChild(new SheetData());
        SheetData.AppendChild(new Row(
            new Cell() { CellValue = new CellValue(DateTime.Today.ToOADate().ToString(CultureInfo.InvariantCulture)), StyleIndex = 1 },
            // Only works for Office 2010+.
            new Cell() { CellValue = new CellValue(DateTime.Today.ToString("s")), DataType = CellValues.Date, StyleIndex = 1 }));

        // Link worksheet to workbook.
        var Sheets = Workbook.AppendChild(new Sheets());
        Sheets.AppendChild(new Sheet()
        {
            Id = WorkbookPart.GetIdOfPart(WorksheetPart),
            SheetId = (uint)(Sheets.Count() + 1),
            Name = "Example"
        });

        Workbook.Save();
    }
}

private static Stylesheet GetStylesheet()
{
    var StyleSheet = new Stylesheet();

     // Create "fonts" node.
    var Fonts = new Fonts();
    Fonts.Append(new Font()
    {
        FontName = new FontName() { Val = "Calibri" },
        FontSize = new FontSize() { Val = 11 },
        FontFamilyNumbering = new FontFamilyNumbering() { Val = 2 },
    });

    Fonts.Count = (uint)Fonts.ChildElements.Count;

    // Create "fills" node.
    var Fills = new Fills();
    Fills.Append(new Fill()
    {
        PatternFill = new PatternFill() { PatternType = PatternValues.None }
        });
        Fills.Append(new Fill()
        {
            PatternFill = new PatternFill() { PatternType = PatternValues.Gray125 }
        });

    Fills.Count = (uint)Fills.ChildElements.Count;

    // Create "borders" node.
    var Borders = new Borders();
    Borders.Append(new Border()
    {
        LeftBorder = new LeftBorder(),
        RightBorder = new RightBorder(),
        TopBorder = new TopBorder(),
        BottomBorder = new BottomBorder(),
        DiagonalBorder = new DiagonalBorder()
    });

    Borders.Count = (uint)Borders.ChildElements.Count;

    // Create "cellStyleXfs" node.
    var CellStyleFormats = new CellStyleFormats();
    CellStyleFormats.Append(new CellFormat()
    {
        NumberFormatId = 0,
        FontId = 0,
        FillId = 0,
        BorderId = 0
    });

    CellStyleFormats.Count = (uint)CellStyleFormats.ChildElements.Count;

    // Create "cellXfs" node.
    var CellFormats = new CellFormats();

    // A default style that works for everything but DateTime
    CellFormats.Append(new CellFormat()
    {
        BorderId = 0,
        FillId = 0,
        FontId = 0,
        NumberFormatId = 0,
        FormatId = 0,
        ApplyNumberFormat = true
    });

   // A style that works for DateTime (just the date)
   CellFormats.Append(new CellFormat()
    {
        BorderId = 0,
        FillId = 0,
        FontId = 0,
        NumberFormatId = 14, // or 22 to include the time
        FormatId = 0,
        ApplyNumberFormat = true
    });

    CellFormats.Count = (uint)CellFormats.ChildElements.Count;

    // Create "cellStyles" node.
    var CellStyles = new CellStyles();
    CellStyles.Append(new CellStyle()
    {
        Name = "Normal",
        FormatId = 0,
        BuiltinId = 0
    });
    CellStyles.Count = (uint)CellStyles.ChildElements.Count;

    // Append all nodes in order.
    StyleSheet.Append(Fonts);
    StyleSheet.Append(Fills);
    StyleSheet.Append(Borders);
    StyleSheet.Append(CellStyleFormats);
    StyleSheet.Append(CellFormats);
    StyleSheet.Append(CellStyles);

    return StyleSheet;
}

【讨论】:

  • 关于 CellValue 值得注意的是,您的选择是国际 Excel 兼容性(123,456 与 123.456)以及与旧版 Office 的向后兼容性(yyyy-MM-ddTHH:mm:ssK 与 ToOADate()) .
  • 这个人太复杂了。我只是放弃并将日期值添加为字符串。
【解决方案2】:

尝试指出它是CellValues.String 类型,而不是CellValues.Date 类型。

使用

DataType = new EnumValue<CellValues>(CellValues.String)   // good

而不是

DataType = new EnumValue<CellValues>(CellValues.Date)     // bad

现在,将其添加为 date 是有意义的,不带 ToString()conversion,并使用 CellValues.Date DataType -- 但 CellValue() only takes a string 作为参数。

[为什么,OpenXmlSDK,为什么???你是一个包装器。把东西包好。让它们隐形,让我的生活更轻松。 :::叹气:::]

此外,如果目标单元格希望格式化日期,我们应该指出它是一个日期。

但我发现虽然 CellValues.StringCellValues.Date 都按预期格式化(相同),但只有 CellValues.Date 会在加载时抛出“不可读的内容”。

我对@9​​87654331@ 方法的任何变化完全没有运气——我最终得到一个五位数的数字,它在电子表格中显示为五位数,而它应该是一个格式化的日期。

我在添加字符串值时收到相同的错误消息,但使用的是CellValues.Number DataType。

【讨论】:

  • 哟!开车通过downvoter!如果您认为这不准确,请解释原因。因为它对我有用。我希望有更好的解决方案。
  • 请给你的答案代码 sn-p,因为它不清楚。
  • @renathy 答案中有一个 code-sn-p。查看前几行;您需要使用“CellValues.String”而不是“CellValues.Date”。剩下的答案是关于答案似乎有点巫毒编码。
【解决方案3】:

尝试dt.ToOADate().ToString().Replace (",", ".") 而不是dt.ToOADate().ToString()

对于一些工作代码示例,请参阅http://www.codeproject.com/KB/office/ExcelOpenXMLSDK.aspx

编辑:

请将您的代码更改为:

dt.ToOADate().ToString(new CultureInfo("en-US"));

【讨论】:

  • 我尝试更改输出字符串和样式索引,但仍然出现错误。
  • 不起作用。我在工作表中看到一个浮点数。关于这个问题没有一个答案。
  • 您可以将new CultureInfo("en-US") 替换为CultureInfo.InvariantCulture
【解决方案4】:

例如,您可以创建自己的带有日期列的 Excel 文件。然后,如果您使用 Open XML SDK 中的 Productivity Tool 打开它,您会发现没有为具有日期值的单元格指定 DataType。这意味着您应该在创建日期单元格时省略 DataType。在这种情况下,还需要将dt.ToOADate().ToString() 作为单元格值传递。

【讨论】:

    【解决方案5】:

    以下代码可用于在电子表格中设置 DateTime 值:

    Cell cell = GetRequiredCell(); // It returns the required Cell
    
    DateTime dtValue = new DateTime(2012, 12, 8);
    
    string strValue = dtValue.ToOADate().ToString().Replace(",", ".");
    // decimal separator change it to "."
    
    cell.DataType = new EnumValue<CellValues>(CellValues.Number);
    cell.CellValue = new CellValue(strValue);
    cell.StyleIndex = 1; 
    

    【讨论】:

    • 这不会编译。
    【解决方案6】:
    private Cell CreateCellWithValue(DateTime columnValue, uint? styleIndex, string cellReference)
    {
        Cell c = new Cell();
        c.DataType = CellValues.Number;
        c.CellValue = new CellValue(columnValue.ToOADate().ToString(new CultureInfo("en-US")));
        c.CellReference = cellReference;
        c.StyleIndex = styleIndex;
    
        return c;
    }
    

    【讨论】:

      【解决方案7】:

      以下对我们有用:

      c.CellValue = new CellValue(datetimeValue).ToOADate().ToString());
      c.DataType = CellValues.Number;
      c.StyleIndex = StyleDate;
      

      DataType 设置为CellValues.Number,然后确保使用CellFormats 中的适当样式索引格式化单元格。在我们的例子中,我们在工作表中构建了一个样式表,StyleDate 是样式表中CellFormats 的索引。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多