OpenXML – 在Excel电子表格中写入日期会导致内容不可读

我使用以下代码将日期时间添加到电子表格中的列:

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

当我尝试在Excel 2010中打开该文件时,我收到错误

 Excel found unreadable content in file.xlsx 

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

我在StackOverFlow上提到了simiiar问题 ,但它们基本上和我一样有相同的代码

像往常一样迟到了派对,但我必须发布一个答案,因为除了Oleh的投票答案之外,所有以前的答案都是完全错误的。

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

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

   42005   

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

  

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

所以生成上面的代码:

  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 ,这里是一些其他格式的列表 。

不幸的是,似乎只添加上述样式还不够,如果你这样做,实际上会导致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 })); 

结果:

   2015-08-05T11:13:57   

完整的代码清单

下面是添加具有日期格式的单元格所需的最小代码示例。

 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.Stylesheet = GetStylesheet(); WorkbookStylesPart.Stylesheet.Save(); // Create worksheet. var WorksheetPart = Spreadsheet.WorkbookPart.AddNewPart(); 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 = 0 }, // Only works for Office 2010+. new Cell() { CellValue = new CellValue(DateTime.Today.ToString("s")), DataType = CellValues.Date, StyleIndex = 0 })); // 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(); CellFormats.Append(new CellFormat() { BorderId = 0, FillId = 0, FontId = 0, NumberFormatId = 14, 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; } 

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

使用

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

代替

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

现在,在没有ToString()转换的情况下将其添加为日期是有意义的,并使用CellValues.Date DataType – 但CellValue()仅将字符串作为参数。 [为什么,OpenXmlSDK, 为什么 ??? 你是一个包装者。 很好地包装好东西。 让它们隐形,让我的生活更轻松。 :::叹:::]

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

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

我对dt.ToOADate().ToString(new CultureInfo("en-US"));任何变化都没有运气dt.ToOADate().ToString(new CultureInfo("en-US")); 方法 – 我最终得到一个五位数的数字,当它应该是格式化的日期时,它会在电子表格中显示为五位数字。

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

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

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

编辑:

请将您的代码更改为:

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

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

  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; } 

以下代码可用于在电子表格中设置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.Number); cell.CellValue = new CellValue(strValue); cell.StyleIndex = 1; 

以下为我们工作:

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

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