Подтвердить что ты не робот

Чтение даты из xlsx с помощью открытого xml sdk

У меня есть дата в формате "4/5/2011" (месяц/день/год) в файле xlsx в одной из ячеек. Im пытается разобрать файл и загрузить эти данные в некоторых классах.

До сих пор часть, где я разбираю ячейку, выглядит так:

string cellValue = cell.InnerText;
if (cell.DataType != null)
{
    switch (cell.DataType.Value)
    {
        case CellValues.SharedString:
            // get string from shared string table
            cellValue = this.GetStringFromSharedStringTable(int.Parse(cellValue));
            break;
    }
}

Я надеялся, что дата будет cell.DataType. По правде говоря, при анализе ячейки с датой "4/5/2011" значение cell.DataType равно null, а значение ячейки - "40638", и это не индекс общей таблицы строк. (Я пробовал это раньше, и это закончилось исключением.)

Любые идеи? Благодаря

4b9b3361

Ответ 1

Open XML хранит даты как количество дней с 1 января 1900 года. Ну, пропуская неверный 29 февраля 1900 года как действительный день. Вы должны уметь находить алгоритмы, которые помогут вам рассчитать правильное значение. Я считаю, что некоторые разработчики используют DateTime.FromOADate() в качестве помощника.

Кроме того, класс Cell имеет свойство DataType как Number по умолчанию. Поэтому, если он null, это число, которое включает даты в нашем случае.

Вы переходите только в таблицу разделяемых строк, когда хранящаяся дата находится до эпохи (в этом случае 1 января 1900 года). И тогда в этом случае класс CellValue класса Cell удерживает индекс в общей таблице строк.

Ответ 2

вы можете использовать DateTime.FromOADate(41690)

Ответ 3

У меня была такая же проблема - переход на EPPlus http://epplus.codeplex.com/

Обратите внимание, что у него есть лицензия LGPL. Поэтому, если вам нужна ваша база кода, чтобы быть в безопасности от проблемы GPL, просто используйте библиотеку, как есть, и ваша исходная лицензия на базовую версию кода безопасна.

Ответ 4

Добавление моего 2 пенсов. Я обрабатываю шаблон, поэтому я знаю, что данная ячейка предназначена для DateTime. Поэтому я заканчиваю этот метод строковым параметром excelDateTime, содержащим значение ячейки, которое обычно будет номером OADate, например "42540.041666666664".

public static bool TryParseExcelDateTime(string excelDateTimeAsString, out DateTime dateTime)
{
    double oaDateAsDouble;
    if (!double.TryParse(excelDateTimeAsString, out oaDateAsDouble)) //this line is Culture dependent!
        return false;
    //[...]
    dateTime = DateTime.FromOADate(oaDateAsDouble);

Моя проблема заключается в том, что конечный пользователь находится в Германии, и поскольку это веб-сайт, мы установили Thread.CurrentThread.CurrentCulture и Thread.CurrentThread.CurrentUICulture в "DE-de". И когда вы вызываете double.TryParse, он использует культуру для разбора номера. Так что эта строка: double.TryParse("42540.041666666664", out oaDate) действительно работает, но возвращает 42540041666666664, как в Германии, точка является разделителем групп. DateTime.FromOADate затем выходит из строя, потому что число вне диапазона (minOaDate = -657435.0, maxOaDate = +2958465.99999999).

Это заставляет меня думать, что:

  • независимо от языкового стандарта на пользовательском компьютере, документ OpenXML содержит номера, отформатированные в стандартном языковом стандарте (в любом случае, инвариант от US, с точкой в ​​виде десятичного разделителя). Я искал, но не нашел спецификацию для этого.
  • при выполнении double.TryParse в потенциальной строке OADate мы должны сделать это с помощью double.TryParse(excelDateTimeAsString, NumberStyles.Any, CultureInfo.InvariantCulture, out oaDateAsDouble)). Я использую CultureInfo.InvariantCulture, но это должно быть то, что точка 1, что я не знаю точно.

Ответ 5

Похоже, что cell.DataType не установлен для дат.

Способ сделать это - посмотреть, есть ли в ячейке StyleIndex, который является индексом массива форматов ячеек в документе.

Затем вы используете cellFormat.NumberFormatId, чтобы увидеть, является ли это тип данных даты.

Вот некоторый код:

    public class ExcelCellWithType
    {
        public string Value { get; set; }
        public UInt32Value ExcelCellFormat { get; set; }
        public bool IsDateTimeType { get; set; }
    }  

    public class ExcelDocumentData
    {
        public ExcelXmlStatus Status { get; set; }
        public IList<Sheet> Sheets { get; set; }
        public IList<ExcelSheetData> SheetData { get; set; }

        public ExcelDocumentData()
        {
            Status = new ExcelXmlStatus();
            Sheets = new List<Sheet>();
            SheetData = new List<ExcelSheetData>();
        }
    } 

    ...

    public ExcelDocumentData ReadSpreadSheetDocument(SpreadsheetDocument mySpreadsheet, ExcelDocumentData data)
    {
        var workbookPart = mySpreadsheet.WorkbookPart;

        data.Sheets = workbookPart.Workbook.Descendants<Sheet>().ToList();

        foreach (var sheet in data.Sheets)
        {
            var sheetData = new ExcelSheetData { SheetName = sheet.Name };
            var workSheet = ((WorksheetPart)workbookPart.GetPartById(sheet.Id)).Worksheet;

            sheetData.ColumnConfigurations = workSheet.Descendants<Columns>().FirstOrDefault();
            var rows = workSheet.Elements<SheetData>().First().Elements<Row>().ToList();
            if (rows.Count > 1)
            {
                foreach (var row in rows)
                {
                    var dataRow = new List<ExcelCellWithType>();

                    var cellEnumerator = GetExcelCellEnumerator(row);
                    while (cellEnumerator.MoveNext())
                    {
                        var cell = cellEnumerator.Current;
                        var cellWithType = ReadExcelCell(cell, workbookPart);
                        dataRow.Add(cellWithType);
                    }

                    sheetData.DataRows.Add(dataRow);
                }
            }
            data.SheetData.Add(sheetData);
        }

        return data;
    }

    ...

    private ExcelCellWithType ReadExcelCell(Cell cell, WorkbookPart workbookPart)
    {
        var cellValue = cell.CellValue;
        var text = (cellValue == null) ? cell.InnerText : cellValue.Text;
        if (cell.DataType?.Value == CellValues.SharedString)
        {
            text = workbookPart.SharedStringTablePart.SharedStringTable
                .Elements<SharedStringItem>().ElementAt(
                    Convert.ToInt32(cell.CellValue.Text)).InnerText;
        }

        var cellText = (text ?? string.Empty).Trim();

        var cellWithType = new ExcelCellWithType();

        if (cell.StyleIndex != null)
        {
            var cellFormat = workbookPart.WorkbookStylesPart.Stylesheet.CellFormats.ChildElements[
                int.Parse(cell.StyleIndex.InnerText)] as CellFormat;

            if (cellFormat != null)
            {
                cellWithType.ExcelCellFormat = cellFormat.NumberFormatId;

                var dateFormat = GetDateTimeFormat(cellFormat.NumberFormatId);
                if (!string.IsNullOrEmpty(dateFormat))
                {
                    cellWithType.IsDateTimeType = true;

                    if (!string.IsNullOrEmpty(cellText))
                    {
                       if (double.TryParse(cellText, out var cellDouble))
                        {
                            var theDate = DateTime.FromOADate(cellDouble);
                            cellText = theDate.ToString(dateFormat);
                        }
                    }
                }
            }
        }

        cellWithType.Value = cellText;

        return cellWithType;
    }

    //// https://msdn.microsoft.com/en-GB/library/documentformat.openxml.spreadsheet.numberingformat(v=office.14).aspx
    private readonly Dictionary<uint, string> DateFormatDictionary = new Dictionary<uint, string>()
    {
        [14] = "dd/MM/yyyy",
        [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",
        [30] = "M/d/yy",
        [34] = "yyyy-MM-dd",
        [45] = "mm:ss",
        [46] = "[h]:mm:ss",
        [47] = "mmss.0",
        [51] = "MM-dd",
        [52] = "yyyy-MM-dd",
        [53] = "yyyy-MM-dd",
        [55] = "yyyy-MM-dd",
        [56] = "yyyy-MM-dd",
        [58] = "MM-dd",
        [165] = "M/d/yy",
        [166] = "dd MMMM yyyy",
        [167] = "dd/MM/yyyy",
        [168] = "dd/MM/yy",
        [169] = "d.M.yy",
        [170] = "yyyy-MM-dd",
        [171] = "dd MMMM yyyy",
        [172] = "d MMMM yyyy",
        [173] = "M/d",
        [174] = "M/d/yy",
        [175] = "MM/dd/yy",
        [176] = "d-MMM",
        [177] = "d-MMM-yy",
        [178] = "dd-MMM-yy",
        [179] = "MMM-yy",
        [180] = "MMMM-yy",
        [181] = "MMMM d, yyyy",
        [182] = "M/d/yy hh:mm t",
        [183] = "M/d/y HH:mm",
        [184] = "MMM",
        [185] = "MMM-dd",
        [186] = "M/d/yyyy",
        [187] = "d-MMM-yyyy"
    };

    private string GetDateTimeFormat(UInt32Value numberFormatId)
    {
        return DateFormatDictionary.ContainsKey(numberFormatId) ? DateFormatDictionary[numberFormatId] : string.Empty;
    }

Ответ 6

Каждая ячейка имеет 2 свойства r (CellReference) и s (StyleIndex)

StyleIndex для чисел - 2, а для даты - 3

Дата в ODate и вы можете преобразовать в строковый формат

value = DateTime.FromOADate(double.Parse(значение)). ToShortDateString();