2011-01-18 78 views
37

我正在閱讀使用Office Open XML SDK的.xlsx文件,並對讀取日期/時間值感到困惑。我的一個電子表格有這個標記(通過Excel 2010中產生)什麼表示Office Open XML單元格包含日期/時間值?

<x:row r="2" spans="1:22" xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> 
    <x:c r="A2" t="s"> 
    <x:v>56</x:v> 
    </x:c> 
    <x:c r="B2" t="s"> 
    <x:v>64</x:v> 
    </x:c> 
    . 
    . 
    . 
    <x:c r="J2" s="9"> 
    <x:v>17145</x:v> 
    </x:c> 

細胞J2中有和日期的序列值的樣式屬性s="9"。但是,Office Open XML規範指出,9對應於後面的超鏈接。這是從ECMA-376,第二版,第1部分 - 基礎和標記語言參考.pdf的第4,999頁的屏幕截圖。

alt text

的presetCellStyles.xml文件包含規範還指builtinId 9作爲遵循超鏈接。

<followedHyperlink builtinId="9"> 

規範中的所有樣式都只是可視格式樣式,而不是數字樣式。數字樣式在哪裏定義,以及如何區分樣式引用s="9"指示單元格格式(視覺)樣式還是數字樣式?

顯然,我正在尋找錯誤的地方來匹配單元格上的樣式與他們的數字格式。哪裏可以找到這些信息?

回答

47

s屬性在styles.xml中引用樣式xf條目。 xf樣式依次引用數字格式掩碼。要標識包含日期的單元格,需要執行樣式xf - > numberformat查找,然後確定該numberformat掩碼是否是日期/時間numberformat掩碼(而不是例如百分比或會計號碼格式掩碼)。

的style.xml文件有相同的元素:

<xf numFmtId="14" ... applyNumberFormat="1" /> 
<xf numFmtId="1" ... applyNumberFormat="1" /> 

這些都是XF的條目,這反過來給你一個numFmtId引用的數字格式掩碼。

你應該找到附近style.xml上方某處numFmts部分,如樣式表元素的一部分

<?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> 

數字格式ID可以在這裏,也可以是一個內置格式。小於164的數字格式代碼(numFmtId)是「內置的」。

,我有這份名單是不完整的:

0 = 'General'; 
1 = '0'; 
2 = '0.00'; 
3 = '#,##0'; 
4 = '#,##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# ??/??'; 

缺失值主要涉及東亞的​​變體形式。

+0

謝謝!非常詳細,正是我需要的。你從哪裏得到了不完整的內置`numFmtId`列表?規範中的完整列表是否在某處?別的地方? – 2011-01-19 01:39:42

+2

內置數字格式的完整列表可以在Ecma Office Open XML文件格式標準文檔(http://www.ecma-international.org/news/TC45_current_work/TC45_available_docs.htm)的第4部分中找到,以供OpenXML部分3.8.30和3.8。31(第2127至2143頁) – 2011-01-19 08:42:24

1

在styles.xml中查看是否存在numFmt節點。我認爲這將持有與所使用的日期格式相關的「9」的numFmtId。

我不知道它在ECMA中的位置,但是如果您搜索numFmt,您可能會發現它。

+0

s =「9」是指xfId,而不是numFmtId – 2011-01-18 23:38:46

-1

我不清楚如何可靠地確定一個單元格是否具有日期/時間值。花了一些時間試驗後,我想出了代碼(see post),它會尋找內置的和自定義的日期/時間格式。

6

選定的答案是專注的,但請注意,Excel定義了一些與OpenXML規範不同的數字格式(numFmt)代碼。每Open XML SDK 2.5生產力工具的文檔(對於NumberingFormat類 「實施者註釋」 選項卡上):

該標準定義了內置的格式ID 14: 「MM-DD-YY」; 22:「m/d/yy h:mm」; 37:「#,## 0;(#,## 0)」; 38:「#,## 0; [Red]」; 39:「#,## 0.00;(#,## 0.00)」; 40:「#,## 0.00; [Red]」; 47:「mmss.0」; KOR fmt 55:「yyyy-mm-dd」。

Excel中定義內置格式ID
14: 「M/d/yyyy的」
22: 「M/d/yyyy的H:毫米」
37:「#,## 0 _);( #,## 0)「
38:」#,## 0 _); [Red]「
39:」#,## 0.00 _);(#,## 0.00)「
40:」# ,## 0.00 _); [紅色]」
47: 「毫米:ss.0」
55: 「YYYY/MM/DD」

大部分都是微小的變化,但#14是一個愚蠢的。我浪費了幾個小時來解決爲什麼前導零不會被添加到單位數月和數天(例如01/05/14比2014年1月5日)。

3

想到我會添加我的解決方案,以便確定雙重值FromOADate是否確實是一個日期。原因是我在我的excel文件中也有一個郵政編碼。如果是文本,numberingFormat將爲空。

或者,您可以使用numberingFormatId並根據Excel用於日期的Ids列表進行檢查。

在我的情況下,我已明確確定客戶端的所有字段的格式。

/// <summary> 
    /// Creates the datatable and parses the file into a datatable 
    /// </summary> 
    /// <param name="fileName">the file upload's filename</param> 
    private void ReadAsDataTable(string fileName) 
    { 
     try 
     { 
      DataTable dt = new DataTable(); 
      using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(string.Format("{0}/{1}", UploadPath, fileName), false)) 
      { 
       WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart; 
       IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>(); 
       string relationshipId = sheets.First().Id.Value; 
       WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId); 
       Worksheet workSheet = worksheetPart.Worksheet; 
       SheetData sheetData = workSheet.GetFirstChild<SheetData>(); 
       IEnumerable<Row> rows = sheetData.Descendants<Row>(); 

       var cellFormats = workbookPart.WorkbookStylesPart.Stylesheet.CellFormats; 
       var numberingFormats = workbookPart.WorkbookStylesPart.Stylesheet.NumberingFormats; 

       // columns omitted for brevity 

       // skip first row as this row is column header names 
       foreach (Row row in rows.Skip(1)) 
       { 
        DataRow dataRow = dt.NewRow(); 

        for (int i = 0; i < row.Descendants<Cell>().Count(); i++) 
        { 
         bool isDate = false; 
         var styleIndex = (int)row.Descendants<Cell>().ElementAt(i).StyleIndex.Value; 
         var cellFormat = (CellFormat)cellFormats.ElementAt(styleIndex); 

         if (cellFormat.NumberFormatId != null) 
         { 
          var numberFormatId = cellFormat.NumberFormatId.Value; 
          var numberingFormat = numberingFormats.Cast<NumberingFormat>() 
           .SingleOrDefault(f => f.NumberFormatId.Value == numberFormatId); 

          // Here's yer string! Example: $#,##0.00_);[Red]($#,##0.00) 
          if (numberingFormat != null && numberingFormat.FormatCode.Value.Contains("mm/dd/yy")) 
          { 
           string formatString = numberingFormat.FormatCode.Value; 
           isDate = true; 
          } 
         } 

         // replace '-' with empty string 
         string value = GetCellValue(spreadSheetDocument, row.Descendants<Cell>().ElementAt(i), isDate); 
         dataRow[i] = value.Equals("-") ? string.Empty : value; 
        } 

        dt.Rows.Add(dataRow); 
       } 
      } 

      this.InsertMembers(dt); 
      dt.Clear(); 
     } 
     catch (Exception ex) 
     { 
      LogHelper.Error(typeof(MemberUploadApiController), ex.Message, ex); 
     } 
    } 

    /// <summary> 
    /// Reads the cell's value 
    /// </summary> 
    /// <param name="document">current document</param> 
    /// <param name="cell">the cell to read</param> 
    /// <returns>cell's value</returns> 
    private string GetCellValue(SpreadsheetDocument document, Cell cell, bool isDate) 
    { 
     string value = string.Empty; 

     try 
     { 
      SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart; 
      value = cell.CellValue.InnerXml; 

      if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString) 
      { 
       return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText; 
      } 
      else 
      { 
       // check if this is a date or zip. 
       // integers will be passed into this else statement as well. 
       if (isDate) 
       { 
        value = DateTime.FromOADate(double.Parse(value)).ToString(); 
       } 

       return value; 
      } 
     } 
     catch (Exception ex) 
     { 
      LogHelper.Error(typeof(MemberUploadApiController), ex.Message, ex); 
     } 

     return value; 
    } 
0

萬一別人是有一個很難與此,這裏是我做了什麼:

1)創建一個新的Excel文件,並把在日期時間字符串在單元格A1

2)將單元格的格式更改爲任何你想要的,然後保存文件。

3)以下PowerShell腳本中提取出從.xlxs樣式

[Reflection.Assembly]::LoadWithPartialName("DocumentFormat.OpenXml") 

$xlsx = (ls C:\PATH\TO\FILE.xlsx).FullName 
$package = [DocumentFormat.OpenXml.Packaging.SpreadsheetDocument]::Open($xlsx, $true) 

[xml]$style = $package.WorkbookPart.WorkbookStylesPart.Stylesheet.OuterXml 
Out-File -InputObject $style.OuterXml -FilePath "style.xml" 

style.xml現在包含的信息,您可以注入到DocumentFormat.OpenXml.Spreadsheet.Stylesheet(string outerXml),導致

4)使用提取的文件來運行構建excel對象模型

var style = File.ReadAllText(@"c:\PATH\TO\EXTRACTED\Style.xml"); 
var stylesheetPart = WorkbookPart_REFERENCE.AddNewPart<WorkbookStylesPart>(); 
stylesheetPart.Stylesheet = new Stylesheet(style); 
stylesheetPart.Stylesheet.Save(); 
相關問題