我的格式为“4/5/2011”(月/日/年)的日期位于其中一个单元格的xlsx文件中。我试图解析文件并在某些类中加载这些数据。
到目前为止,我解析单元格的部分如下所示:
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;
}
}
我希望date可以是cell.DataType。事实是,在使用日期“4/5/2011”解析单元格时,cell.DataType的值为null,单元格的值为“40638”,并且它不是共享字符串表的索引。 (我之前尝试过这种方法,结果是例外。)
有什么想法吗? 感谢
答案 0 :(得分:32)
Open XML将日期存储为1900年1月1日的天数。好吧,将1900年2月29日的错误日期作为有效日期。您应该能够找到算法来帮助您计算正确的值。我相信一些开发人员使用DateTime.FromOADate()
作为帮助。
此外,默认情况下,Cell
类的DataType
属性为Number。因此,如果它为null,则为数字,其中包括我们的日期。
当存储的日期早于纪元(1900年1月1日)时,您只前往共享字符串表。然后在这种情况下,Cell类的CellValue保存共享字符串表的索引。
答案 1 :(得分:10)
您可以使用DateTime.FromOADate(41690)
答案 2 :(得分:3)
我有同样的问题 - 切换到EPPlus http://epplus.codeplex.com/
请注意,它具有LGPL许可证。因此,如果您需要保护代码库免受GPL问题的影响,只需按原样使用该库,并且您的原始代码库许可证是安全的。
答案 3 :(得分:1)
似乎未为日期设置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;
}
答案 4 :(得分:0)
加上我2便士的价值。 我正在处理一个模板,所以我知道给定的单元格是一个DateTime。 所以我最终在这个方法中使用包含单元格值的字符串参数excelDateTime,这通常是一个OADate数字,如&#34; 42540.041666666664&#34;。
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设置为&#34; DE-de&#34; 。当您致电double.TryParse
时,它会使用文化来解析数字。所以这一行:double.TryParse("42540.041666666664", out oaDate)
确实有效,但它返回42540041666666664
,因为在德国,dot是一个组分隔符。 DateTime.FromOADate
然后失败,因为该数字超出了范围(minOaDate = -657435.0, maxOaDate = +2958465.99999999)。
这让我想到:
double.TryParse
时,我们应该使用double.TryParse(excelDateTimeAsString, NumberStyles.Any, CultureInfo.InvariantCulture, out oaDateAsDouble))
。我正在使用CultureInfo.InvariantCulture,但它应该是1点,我不确定。答案 5 :(得分:0)
我在检索任何内联字符串之后执行此操作:
private static object Convert(this DocumentFormat.OpenXml.Spreadsheet.CellValues value, string content)
{
switch (value)
{
case DocumentFormat.OpenXml.Spreadsheet.CellValues.Boolean:
if (content.Length < 2)
{
return content?.ToUpperInvariant() == "T" || content == "1";
}
return System.Convert.ToBoolean(content);
case DocumentFormat.OpenXml.Spreadsheet.CellValues.Date:
if (double.TryParse(content, out double result))
{
return System.DateTime.FromOADate(result);
}
return null;
case DocumentFormat.OpenXml.Spreadsheet.CellValues.Number:
return System.Convert.ToDecimal(content);
case DocumentFormat.OpenXml.Spreadsheet.CellValues.Error:
case DocumentFormat.OpenXml.Spreadsheet.CellValues.String:
case DocumentFormat.OpenXml.Spreadsheet.CellValues.InlineString:
case DocumentFormat.OpenXml.Spreadsheet.CellValues.SharedString:
default:
return content;
}
}
答案 6 :(得分:-1)
每个单元格有2个属性r(CellReference)和s(StyleIndex)
数字的StyleIndex为2,日期为3
日期在ODate中,您可以转换为字符串格式
value = DateTime.FromOADate(double.Parse(value))。ToShortDateString();