Qu'est-ce qui indique qu'une cellule XML ouverte contient une Date/heure?

je lis un .xlsx fichier à l'aide de la Office Open XML SDK et je suis confus au sujet de la lecture des valeurs de Date/Heure. Un de mes tableurs a ce markup (généré par 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>
La cellule

J2 contient une valeur de série de date et un attribut de style s="9" . Toutefois, la spécification Office Open XML indique que 9 correspond à un hyperlien suivi. Ceci est un cliché d'écran de la page 4 999 De ECMA-376, Second Edition, Partie 1 - Fondements Et Référence De Langage De Balisage.pdf .

alt text

the presetCellStyles.le fichier xml inclus dans le spec fait également référence à builtinId 9 sous la forme d'un hyperlien suivi.

<followedHyperlink builtinId="9">

tous les styles de la spécification sont simplement des styles de formatage visuel, pas des styles de nombres. Où sont les styles de nombre définis et comment distingue - t-on une référence de style s="9" d'indiquer un style de mise en forme de cellules (visuel) par rapport à un style de nombre?

évidemment, je cherche au mauvais endroit pour faire correspondre les styles sur les cellules avec leurs formats de nombres. Où est le bon endroit pour trouver cette information?

40
demandé sur mwag 2011-01-19 02:14:40

6 réponses

l'attribut s renvoie à une entrée style xf dans styles.XML. Le style xf renvoie à son tour à un masque de Format Nombre. Pour identifier une cellule qui contient une date, vous devez effectuer le style XF -> numberformat lookup, puis identifier si ce masque de numberformat est un masque de date/heure (plutôt que, par exemple, un pourcentage ou un masque de NumberFormat comptable).

le style.le fichier xml contient des éléments comme:

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

ce sont les entrées xf, qui à leur tour vous donnent un numFmtId qui fait référence au masque de format de nombre.

, Vous devriez trouver la numFmts quelque part près du haut de style.xml, faisant partie de l'élément de feuille de style

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

le numéro d'identification de format peut être ici, ou il peut être l'un des formats intégrés. Les codes de format de numéro (numFmtId) inférieurs à 164 sont "intégrés".

la liste que j'ai est incomplète:

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

les valeurs manquantes sont principalement liées aux formats de variante de l'Asie de l'est.

50
répondu Mark Baker 2011-01-18 23:37:06

la réponse choisie est spot-on, mais notez Qu'Excel définit certains codes de format numFmt (number format) différemment de la spécification OpenXML. Selon la documentation Open XML SDK 2.5 outil de productivité (sur l'onglet "Notes D'implémentation" pour la classe NumberingFormat):

La norme définit intégré dans le code de format 14: "mm-jj-aa"; 22: "j/m/aa hh:mm"; 37: "#,##0 ;(#,##0)"; 38: "#,##0 ;[Rouge]"; 39: "#,##0.00;(#,##0.00)"; 40: "#,##0.00;[Rouge]"; 47: "des mms.0"; KOR fmt 55:"AAAA-mm-JJ".

Excel définit le format intégré ID

14: "m/d / yyyy"

22: "m/d/yyyy hh:mm"

37: "#,##0_);(#,##0)"

38: "#,##0_);[Rouge] "

39: "#,##0.00_);(#,##0.00)"

40: "#,##0.00_);[Rouge] "

47: "mm: SS.0 "

Cinquante cinq: "aaaa/mm/jj"

la plupart sont des variantes mineures, mais #14 est un doozy. J'ai perdu quelques heures à résoudre pourquoi des zéros de tête n'étaient pas ajoutés à des mois et des jours à un chiffre (par exemple 01/05/14 vs 1/5/14).

6
répondu softwaredev 2014-04-22 08:21:45

a pensé que je voudrais ajouter ma solution que j'ai mis en place pour déterminer si la double valeur FromOADate est vraiment une date ou non. La raison est que j'ai aussi un code postal dans mon fichier excel. Le numberingFormat sera nul si c'est du texte.

vous pouvez aussi utiliser le numberingFormatId et vérifier une liste de Ids que Excel utilise pour les dates.

dans mon cas, j'ai explicitement déterminé le formatage de tous les champs pour le client.

    /// <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;
    }
4
répondu Rob Scott 2015-02-12 18:32:52

dans styles.xml voir s'il y a un noeud numFmt. Je pense que cela tiendra un numFmtId de "9" qui se rapportera au format de date qui est utilisé.

Je ne sais pas où c'est dans L'ECMA, mais si vous cherchez numFmt, vous pourriez le trouver.

1
répondu Dick Kusleika 2011-01-18 23:30:36

dans le cas où quelqu'un d'autre est avoir un moment difficile avec cela, voici ce que j'ai fait:

1) Créer un nouveau fichier excel et mettre dans une chaîne de date heure dans la cellule A1

2) changez le formatage sur la cellule à ce que vous voulez, puis enregistrez le fichier.

3) Exécutez en suivant le script powershell pour extraire la feuille de style .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 contient maintenant les informations que vous pouvez injecter pour DocumentFormat.OpenXml.Spreadsheet.Stylesheet(string outerXml) , conduisant à

4) Utiliser le fichier extrait pour construire le modèle d'objet 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();
0
répondu Frison Alexander 2015-04-20 11:48:41

Je ne savais pas comment déterminer de façon fiable si une cellule avait une valeur date/heure. Après avoir passé du temps à expérimenter, j'ai trouvé le code (voir post) qui recherchait à la fois les formats de date/heure intégrés et personnalisés.

-1
répondu Gordon1000 2017-05-23 11:47:21