从OpenXml Excel文件中读取日期

时间:2011-01-11 08:24:10

标签: xml vb.net excel .net-2.0 openxml

我正在尝试使用SharpZipLib读取.xlsx文件中的数据以解压缩(在内存中)并读取内部xml文件。一切都很好,但识别日期 - 它们以julean格式存储,我需要以某种方式识别数字是日期还是只是数字。在另一个主题(不幸的是它已经死了,我需要快速回答)我从Mark Ba​​ker那里了解了一些东西,但它仍然不够......

  

“Excel将日期存储为浮点值...整数部分是自19/1年1月1日以来的天数(或1/1/1904,具体取决于使用的日历),小数部分是比例一天(即时间部分)...由于1900年被认为是闰年这一事实略显尴尬。

     

区分数据和数字的唯一方法是数字格式掩码。如果您可以阅读格式掩码,您可以使用它来将值标识为日期而不是数字...然后从基准日期计算日期值/格式。“

     

“但是日期的属性”s“是否总是值为”1“?我知道它定义了样式,但也许?;)”

     

s属性引用styles.xml中的样式xf条目,并且日期并不总是条目1 ...这取决于工作簿中使用了多少不同的样式。样式xf又引用数字格式掩码。要识别包含日期的单元格,您需要执行样式xf - >数字格式查找,然后确定该数字格式掩码是否是日期/时间数字格式掩码(而不是例如百分比或会计数字格式掩码)

     

“还有一个问题 - 我现在正在查看style.xml的内容,在我看到的元素中,我看到的元素如下:”< xf numFmtId =“14”... applyNumberFormat =“1 “/>”,“< xf numFmtId =”1“... applyNumberFormat =”1“/>”等,但没有< numFmts>部分...是否有任何“标准”格式?或者我错过了什么?“

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

4 个答案:

答案 0 :(得分:11)

你应该在style.xml顶部附近找到numFmts部分,作为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# ??/??';

答案 1 :(得分:8)

细胞可能有样式。这些是在styleSheet中索引cellXfs的uint。每个cellXfs项都包含一组属性。最重要的是NumberFormatID。如果其值落在14-22范围内,则为“标准”日期。如果它在165 - 180范围内,则为“格式化”日期,并具有相应的NumberingFormat属性。

标准日期

[x:c r =“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)

格式化日期

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

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

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

此代码提取与这些日期格式对应的样式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++;
     }

答案 2 :(得分:4)

我建议numFmtId =“14”应该被认为是“Windows短日期格式”,因为在澳大利亚这种格式会显示日期为“dd / mm / yy”,而不是“mm / dd / yy” ”。

答案 3 :(得分:1)

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

首先抓住&#34; s&#34;或StyleIndex。请注意以下数字原始格式的日期(40667):

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

&#34; s&#34;单元格节点中的属性指向从0开始的从零开始的styles.xml节点数组。这是定位映射到原始数字日期数据的日期格式(如果有)的关键。您看到s = 1,它指向Excel工作簿的以下单元格格式styles.xml部分中的第二个xf节点:

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

在第二个节点中,您会看到numFmtId =&#34; 14&#34;值。那是numberFormatID。它告诉您这是确定您的日期编号应该显示的ID所需的ID。但是该数字指向日期格式的两个可能位置。如果它的数量在14-22范围内,则为日期内置式。如果它的外部范围是(可能)由excel文件所有者添加的自定义日期格式。在检查这两个地方之前你不会知道。

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

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

此时您知道它的日期以及要呈现的格式。如果它不是其中一个值,则可能是自定义数字。现在,您必须再次搜索styles.xml文件,以获得具有匹配numFmtId值的样式节点。这些节点将包含自定义日期格式,如下所示:

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

请注意,如果numFmtId为164,则会找到其自定义日期格式。因此,为了捕捉所有这些疯狂的日期格式,定制和内置,您最好的选择是保持一系列可接受的格式&#34;作为字符串,找到你的formatCode,然后看看它是否与你代码中可接受的那个匹配。

祝你好运!