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

发布于 2024-11-29 21:14:45 字数 627 浏览 0 评论 0原文

我使用以下代码将 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 上提到了类似问题 ,但他们的代码基本上和我一样。

I am using the following code to add a DateTime to a column in my spreadsheet:

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

When I try to open the file in Excel 2010, I get the error

Excel found unreadable content in file.xlsx

All is fine if I comment out the line.

I have referred to similar questions on StackOverflow, but they basically have the same code as I do.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

温折酒 2024-12-06 21:14:45

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

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

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

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

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

<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,这里是 < href="https://stackoverflow.com/questions/4730152/what-indicates-an-office-open-xml-cell-contains-a-date-time-value#4730266">其他一些列表格式。

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

Late to the party as usual but I have to post an answer because ALL of the previous ones are completely wrong except for Oleh's down voted answer which was sadly incomplete.

As the question is related to Excel, the easiest thing to do is create an Excel spread sheet with the data and style you want, then open it as parts and look at the raw XML.

Adding the date 01/01/2015 into cell A1 results in the following:

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

Note that the type attribute is not there. However there is a style attribute referencing the following style:

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

That is the most basic style that you must add.

So code to generate the above:

  1. You need to create a style as follows:
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);

The NumberFormatId = 14 refers to the built-in format mm-dd-yy, here's a list of some other formats.

Unfortunately it seems that adding just the above style is not quite enough and if you do it actually causes Excel to crash. Note that BorderId, FillId, FontId need to correspond to an item in the style sheet which means you need to provide them. The GetStyleSheet() method in the complete code listing provides the minimum default stylesheet required for Excel to work without errors.

  1. And add a cell as follows:
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
    }));

Note: Office 2010 and 2013 can handle dates differently but by default it seems they do not.

They provide support for dates in ISO 8601 format i.e. yyyy-MM-ddTHH:mm:ss just so happens that this is also standard format sortable ("s") so you can do:

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

The result:

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

Complete Code Listing

Below is an example of the minimum code required to add a cell with date format.

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;
}
始终不够 2024-12-06 21:14:45

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

使用

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

而不是

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

Now,将其添加为 date 是有意义的,无需 ToString() 转换,并使用 CellValues.Date DataType -- 但是 CellValue() 仅采用字符串 作为参数。

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

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

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

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

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

Try indicating it is a CellValues.String type, instead of a CellValues.Date type.

Use

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

instead of

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

Now, it would make sense to add it as date, without the ToString()conversion, and use the CellValues.Date DataType -- but CellValue() only takes a string as a parameter.

[Why, OpenXmlSDK, WHY??? You're a wrapper. Wrap things nicely. Make them invisible, and make my life easier. :::sigh:::]

Additionally, if the target cell expects to be formatting a date, we should be indicating it is a date.

But I've found that while CellValues.String and CellValues.Date both get formatted as expected (identically), only the CellValues.Date throws up the "unreadable content" on-load.

I've had utterly no luck with any variation on dt.ToOADate().ToString(new CultureInfo("en-US")); method -- I end up with a five-digit number that gets displayed in the spreadsheet as five-digit number, when it should be a formatted date.

I was receiving the same error message when adding a string value, but using the CellValues.Number DataType.

梅窗月明清似水 2024-12-06 21:14:45

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

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

编辑:

请将您的代码更改为:

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

try dt.ToOADate().ToString().Replace (",", ".") instead of dt.ToOADate().ToString()

For some working code samples see http://www.codeproject.com/KB/office/ExcelOpenXMLSDK.aspx

EDIT:

please change your code to this:

dt.ToOADate().ToString(new CultureInfo("en-US"));
骄傲 2024-12-06 21:14:45

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

As an example you can create your own excel file with date column. Then if you open it with Productivity Tool from Open XML SDK you will find there is no DataType specified for the cell with date value. This means that you should omit DataType when creating date cell. In this case it is also required to pass dt.ToOADate().ToString() as a cell value.

冷︶言冷语的世界 2024-12-06 21:14:45

以下代码可用于在电子表格中设置日期时间值:

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; 

The following code can be used to set a DateTime value in a spreadsheet:

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; 
GRAY°灰色天空 2024-12-06 21:14:45
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;
}
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;
}
谁把谁当真 2024-12-06 21:14:45

以下内容对我们有用:

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

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

The following worked for us:

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

Set the DataType to CellValues.Number and then be sure to format the cell with the appropriate style index from the CellFormats. In our case we build a stylesheet within the worksheet, and StyleDate is an index into the CellFormats in the stylesheet.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文