从OpenXml Excel文件中读取date

我想使用SharpZipLib从.xlsx文件中读取数据,将其解压缩(在内存中)并读取内部xml文件。 一切都很好,但承认date – 他们存储在julean格式,我需要以某种方式识别,如果一个数字是一个date或只有一个数字。 在另一个话题(不幸的是它死了,我需要快速的回答)我知道一些从马克·贝克的东西,但它还不够…

“Excel将date存储为浮点值…整数部分是从1/1/1900(或1/1/1904取决于正在使用哪个日历)以来的天数,小数部分是一天中的比例(即时间部分)…… 1900年被认为是闰年这一事实稍微有些尴尬。

数字与数字唯一区分的是数字格式掩码。 如果您可以阅读格式掩码,则可以使用该格式将该值标识为date而不是数字,然后从基准date计算date值/格式。

“但date的属性”s“的值总是”1“吗?我知道它定义了风格,但也许?)”

s属性引用styles.xml中的style xf条目,并不总是date条目1 …这一切都取决于在工作簿中使用了多less种不同的样式。 xf样式依次引用数字格式掩码。 要标识包含date的单元格,您需要执行样式xf – > numberformat查找,然后确定该numberformat掩码是否是date/时间numberformat掩码(而不是例如百分比或会计号码格式掩码)

“另外一个问题 – 我现在正在查看style.xml的内容,在部分我看到像这样的元素:”<xf numFmtId =“14”… applyNumberFormat =“1”/>“,”<xf numFmtId = “1”… applyNumberFormat =“1”/>“等,但没有<numFmts>部分…有没有”标准“格式?或者我只是想念一些东西?

有人可以帮我吗? 提前致谢。

您应该在style.xml顶部附近的某处findnumFmts部分,作为styleSheet元素的一部分

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <numFmts count="3"> <numFmt numFmtId="164" formatCode="[$-414]mmmm\ yyyy;@" /> <numFmt numFmtId="165" formatCode="0.000" /> <numFmt numFmtId="166" formatCode="#,##0.000" /> </numFmts> 

编辑

我已经仔细检查了我的xlsx阅读器代码(自从我深入到图书馆的那一部分已经有很长一段时间了)。 并有内置的格式。 小于164的数字格式代码(numFmtId)是“内置的”。

我拥有的名单是不完整的:

 0 = 'General'; 1 = '0'; 2 = '0.00'; 3 = '#,##0'; 4 = '#,##0.00'; 5 = '$#,##0;\-$#,##0'; 6 = '$#,##0;[Red]\-$#,##0'; 7 = '$#,##0.00;\-$#,##0.00'; 8 = '$#,##0.00;[Red]\-$#,##0.00'; 9 = '0%'; 10 = '0.00%'; 11 = '0.00E+00'; 12 = '# ?/?'; 13 = '# ??/??'; 14 = 'mm-dd-yy'; 15 = 'd-mmm-yy'; 16 = 'd-mmm'; 17 = 'mmm-yy'; 18 = 'h:mm AM/PM'; 19 = 'h:mm:ss AM/PM'; 20 = 'h:mm'; 21 = 'h:mm:ss'; 22 = 'm/d/yy h:mm'; 37 = '#,##0 ;(#,##0)'; 38 = '#,##0 ;[Red](#,##0)'; 39 = '#,##0.00;(#,##0.00)'; 40 = '#,##0.00;[Red](#,##0.00)'; 44 = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)'; 45 = 'mm:ss'; 46 = '[h]:mm:ss'; 47 = 'mmss.0'; 48 = '##0.0E+0'; 49 = '@'; 27 = '[$-404]e/m/d'; 30 = 'm/d/yy'; 36 = '[$-404]e/m/d'; 50 = '[$-404]e/m/d'; 57 = '[$-404]e/m/d'; 59 = 't0'; 60 = 't0.00'; 61 = 't#,##0'; 62 = 't#,##0.00'; 67 = 't0%'; 68 = 't0.00%'; 69 = 't# ?/?'; 70 = 't# ??/??'; 

单元格可能有样式。 这些是在styleSheet中索引cellXfs的提示。 每个cellXfs项目都包含一组属性。 最重要的是NumberFormatID。 如果它的价值在14-22的范围内,这是一个“标准”date。 如果它落在165-180的范围内,它是一个“格式化的”date,并具有相应的NumberingFormat属性。

标准date

[x:cr =“A2”s =“2”] [x:v] 38046 [/ x:v] [/ x:c]

[x:xf numFmtId =“14”fontId =“0”fillId =“0”borderId =“0”xfId =“0”applyNumberFormat =“1”/](顺序位置2)

格式化date

[x:cr =“A4”s =“4”] [x:v] 38048 [/ x:c] [/ x:c]

[x:xf numFmtId =“166”fontId =“0”fillId =“0”borderId =“0”xfId =“0”applyNumberFormat =“1”/](顺序位置4)

[x:numFmt numFmtId =“166”formatCode =“m / d; @”/]

此代码提取与这些date格式相对应的样式ID列表。

  private void GetDateStyles() { // // The only way to tell dates from numbers is by looking at the style index. // This indexes cellXfs, which contains NumberFormatIds, which index NumberingFormats. // This method creates a list of the style indexes that pertain to dates. WorkbookStylesPart workbookStylesPart = (WorkbookStylesPart) UriPartDictionary["/xl/styles.xml"]; Stylesheet styleSheet = workbookStylesPart.Stylesheet; CellFormats cellFormats = styleSheet.CellFormats; int i = 0; foreach (CellFormat cellFormat in cellFormats) { uint numberFormatId = cellFormat.NumberFormatId; if ((numberFormatId >= 14 && numberFormatId <= 22) || (numberFormatId >= 165u && numberFormatId <= 180u)) { _DateStyles.Add(i.ToString()); } i++; } 

我build议numFmtId =“14”应该被认为是“Windows短date格式”,如在澳大利亚这种格式将显示date为“dd / mm / yy”,而不是“mm / dd / yy”。

有两种方法可以获取单元格的date格式。

你首先抓住“s”或StyleIndex。 请注意以下数字原始格式的date(40667):

 <row r="1"> <cr="A1" s="1"> <v>40667</v> </c> </row> 

单元格节点中的“s”属性指向从0开始的从零开始的styles.xml节点数组。这是查找映射到原始数值date数据的date格式(如果有的话)的关键。 您看到s = 1,它指向Excel工作簿的以下单元格格式styles.xml部分中的第二个xf节点:

  <cellXfs count="2"> <xf numFmtId="0" ... /> <xf numFmtId="14" ... /> </cellXfs> 

在第二个节点中,您会看到numFmtId =“14”的值。 这是numberFormatID。 它告诉你,这是确定你的date编号应该在什么位置所需的id。但是这个数字指向date格式的两个可能的位置。 如果它的数量在14-22的范围内,那么它是一种内置式的date。 如果它的范围外(可能)由Excel文件所有者添加的自定义date格式。 你不知道,直到你检查两个地方。

在第一种情况下,如果它的值为14-22,则需要将其映射到每个excel文件具有(mm-dd-yy等)的预builddate格式之一。 您可以在OpenXML SDK中find该表。 下面是一个numFmtId映射到内置date格式的示例。

 14 mm-dd-yy 15 d-mmm-yy 16 d-mmm 17 mmm-yy 18 h:mm AM/PM 

在这一点上,你知道它的date和格式,如果它不是其中一个值,它可能是一个自定义的数字。 您现在必须再次searchstyles.xml文件,以查找具有匹配的numFmtId值的样式节点。 这些节点将包含自定义date格式,如下所示:

  <numFmts count="2"> <numFmt numFmtId="164" formatCode="mm/yyyy;@" /> <numFmt numFmtId="165" formatCode="0.000" /> <numFmt numFmtId="166" formatCode="#,##0.000" /> </numFmts> 

请注意,如果您的numFmtId是164,您find了自定义的date格式。 所以要抓住所有这些疯狂的date格式,自定义和内置的,你最好的select是保持一系列可接受的“格式”作为string,find你的formatCode,然后看看它是否符合你的代码中的一个可接受的格式。

祝你好运!