это будет искать как встроенные и пользовательские форматы даты / времени.

аю файл .xlsx, используяOffice Open XML SDK и я запутался в чтении значений даты / времени. Одна из моих электронных таблиц имеет эту разметку (сгенерирован Excel 2010)

<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 соответствует гиперссылке. Это снимок экрана со страницы 4,999 изECMA-376, второе издание, часть 1 - Основы и язык разметки. Справочник.pdf.

Файл presetCellStyles.xml, включенный в спецификацию, также относится кbuiltinId 9 в виде гиперссылки.

<followedHyperlink builtinId="9">

Все стили в спецификации являются просто визуальными стилями форматирования, а не числовыми стилями. Где определены числовые стили и как дифференцировать ссылку на стильs="9" от указания стиля форматирования ячейки (визуальный) против стиля числа?

Очевидно, я смотрю не в том месте, чтобы сопоставлять стили ячеек с их числовыми форматами. Где правильное место, чтобы найти эту информацию?

Ответы на вопрос(6)

но обратите внимание, что Excel определяет некоторые коды числового формата (numFmt) не так, как в спецификации OpenXML. В соответствии сOpen XML SDK 2.5 Документация по инструменту повышения производительности (на вкладке «Замечания разработчика» для класса NumberingFormat):

Стандарт определяет встроенный формат ID 14: «мм-дд-гг»; 22: «м / д / гг ч: мм»; 37: "#, ## 0; (#, ## 0)"; 38: "#, ## 0; [Красный]"; 39: "#, ## 0,00; (#, ## 0,00)"; 40: "#, ## 0,00; [Красный]"; 47: "ммсс.0"; KOR fmt 55: "гггг-мм-дд".

Excel определяет идентификатор встроенного формата
14: "м / д / гггг"
22: "м / д / гггг ч: мм"
37: "#, ## 0 _); (#, ## 0)"
38: "#, ## 0 _); [Красный]"
39: "#, ## 0,00 _); (#, ## 0,00)"
40: "#, ## 0,00 _); [Красный]"
47: "мм: сс.0"
55: "гггг / мм / дд"

Большинство из них незначительные изменения, но № 14 является глупым. Я потратил пару часов на устранение неисправностей, почему начальные нули не добавлялись к однозначным месяцам и дням (например, 01/05/14 против 1/5/14).

Решение Вопроса

в свою очередь, ссылается на маску числового формата. Чтобы идентифицировать ячейку, содержащую дату, необходимо выполнить поиск в формате xf -> numberformat, а затем определить, является ли эта маска форматирования чисел форматом даты / времени (а не, например, процентной маской или маской форматирования учетного номера).

Файл style.xml имеет такие элементы, как:

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

Это записи xf, которые, в свою очередь, дают вам numFmtId, который ссылается на маску числового формата.

Вы должны найти раздел numFmts где-то в верхней части style.xml, как часть элемента 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>

Идентификатор формата номера может быть здесь, или это может быть один из встроенных форматов. Коды числового формата (numFmtId) менее 164 являются «встроенными».

Список, который у меня есть, неполон:

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# ??/??';

Недостающие значения в основном связаны с восточноазиатскими вариантами форматов.

 Matt Connolly25 янв. 2012 г., 01:14
Текущая версия может быть загружена с:ecma-international.org/publications/standards/Ecma-376.htm
 Samuel Neff19 янв. 2011 г., 02:39
Спасибо! Очень подробно, именно то, что мне было нужно. Где ты достал неполный встроенныйnumFmtId список? Полный список где-нибудь в спецификации? Где-нибудь еще?
 Mark Baker19 янв. 2011 г., 09:42
Полный список встроенных числовых форматов можно найти в части 4 стандартных форматов файлов Ecma Office Open XML (ecma-international.org/news/TC45_current_work/...) для разделов OpenXML 3.8.30 и 3.8.31 (страницы 2127–2143)
 Samuel Neff19 янв. 2011 г., 21:28
еще раз спасибо. Я нашел список вECMA-376, второе издание, часть 1 - основы и справочник по языку разметки раздел 18.8.30 стр. 1964.
 Matt Connolly25 янв. 2012 г., 01:21
См. Раздел 18.8.30 в текущей спецификации (часть 1, страница 1971).

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

как надежно определить, имеет ли ячейка значение даты / времени. Потратив некоторое время на эксперименты, я придумал код(см. пост) это будет искать как встроенные и пользовательские форматы даты / времени.

я бы добавил свое решение, которое я собрал, чтобы определить, является ли двойное значениеFromOADate действительно свидание или нет. Причина в том, что у меня также есть почтовый индекс в файле Excel.numberingFormat будет нулевым, если это текст.

В качестве альтернативы вы можете использоватьnumberingFormatId и проверить списокIds что Excel использует для дат.

В моем случае я явно определил форматирование всех полей для клиента.

    /// <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;
    }
 Philipp Munin11 мар. 2016 г., 19:27
Я получаю NumberFormatId = 14, и в списке нет элемента с NumberingFormat.Id == 14

существует ли узел numFmt. Я думаю, что он будет содержать numFmtId «9», который будет относиться к используемому формату даты.

Я не знаю, где это находится в ECMA, но если вы ищете numFmt, вы можете найти его.

 Mark Baker19 янв. 2011 г., 00:38
s = "9" относится к xfId, а не к numFmtId

Ваш ответ на вопрос