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

时间:2011-08-17 08:21:37

标签: c# .net openxml

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

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 found unreadable content in file.xlsx

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

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

7 个答案:

答案 0 :(得分:24)

像往常一样迟到了派对,但我必须发布一个答案,因为以前的所有答案都是完全错误的,除非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. 您需要按如下方式创建样式:
  2. 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

    不幸的是,似乎添加只是上面的样式还不够,如果你这样做,实际上会导致Excel崩溃。请注意,BorderIdFillIdFontId需要与样式表中的项目相对应,这意味着您需要提供这些项目。完整代码清单中的GetStyleSheet()方法提供了Excel无错误工作所需的最小默认样式表。

    1. 按如下方式添加单元格:
    2. 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恰好这也是标准格式可排序(&#34; s&#34;)所以你可以这样做:

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

答案 1 :(得分:6)

尝试表明它是CellValues.String类型,而不是CellValues.Date类型。

使用

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

而不是

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

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

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

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

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

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

答案 2 :(得分:5)

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

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

编辑:

请将您的代码更改为:

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

以下代码可用于在电子表格中设置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; 

答案 5 :(得分:0)

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

答案 6 :(得分:-2)

以下为我们工作:

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

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