如何在Epplus ExcelWorksheet中查找日期时间值

时间:2016-05-28 05:55:19

标签: c# epplus

我在MVC 5应用程序中有一个有效的ExcelPackage函数,我可以成功地将强类型模型输出到新的Excel文件。

我遇到一个特定列可以为空的DateTime,它有四个可能的值:

  • 仅限日期
  • 仅限时间
  • 日期和时间

我已经提取了一个函数来完成工作表中任何DateTime值的识别工作。虽然DateTime值一致地被识别和格式化,但我不禁想到可能有更好的方法来实现与嵌套循环相同的结果。

因为这个函数(和包含类)被设计为接受任何IEnumerable,我无法预测哪个列 - 或者甚至 - IEnumerable将包含DateTime值。出于这个原因,我无法对已知范围的细胞进行硬编码。

为了提供更好的上下文,将类构造函数粘贴到此处,然后是我想要更好的函数。即使你拿走了这些评论,它仍然是一套非常丑陋的嵌套代码。

总之,我的问题是:我是否在C#语言或Nuget Epplus包中错过了更优雅或更简单的编码方法?

public class EpplusExcelPackage<T>
{
    private IEnumerable<T> _data;
    private string _reportName;

    public EpplusExcelPackage(IEnumerable<T> Data, string ReportName)
    {
        this._data = Data;
        this._reportName = ReportName;    
    }

    // much more code...

这是我希望提高效率的方法:

private static void FormatDateTimeValuesInWorksheet(ExcelWorksheet worksheet)
{
    /* correctly format datetime values as:
     *     if date only, format as shortdate 
     *     if time only, format as am/pm time
     *     if date & time present, format as default datetime */

    // the worksheet is data is a strongly-typed model, populated in the model constructor

    System.DateTime dateValue; // used as the out variable of DateTime.TryParse()

    // nested for-loop to find datetime values in worksheet
    for (int i = worksheet.Dimension.Start.Column; i < worksheet.Dimension.End.Column; i++)
    {
        for (int j = worksheet.Dimension.Start.Row; j < worksheet.Dimension.End.Row; j++)
        {
            // ignore null cell values to prevent null exception error
            if (worksheet.Cells[i, j].Value != null)
            {
                // convert the cell value to string: required by TryParse()
                string cellValue = worksheet.Cells[i, j].Value.ToString();

                // identify type of datetime and format accordingly
                if (DateTime.TryParse(cellValue, out dateValue))
                {
                    if (dateValue.Date == Convert.ToDateTime("12/30/1899"))
                    {
                        worksheet.Cells[i, j].Value = dateValue.ToShortTimeString();
                    }
                    else if (dateValue.TimeOfDay.TotalSeconds == 0)
                    {
                        worksheet.Cells[i, j].Value = dateValue.ToShortDateString();
                    }
                    else // do not change
                    {
                        worksheet.Cells[i, j].Value = worksheet.Cells[i, j].Value;
                    }
                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:4)

归结为您对源excel表格“正确”格式化的信心。我的意思是它们存储为正确的日期(即数字),或者你可能有“数字存储为字符串”的常见问题。

如果数据通常是干净的,那么你可以通过检查它们的类型来避免你对字符串和日期进行大量的转换。即使这并非完全直截了当,因为Epplus在导入日期时喜欢自己解释。

看看这张表(专注于col A):

enter image description here

第1 - 4行具有“正确”格式化的数据。这意味着日期和时间作为双精度存储在excel中。第5-8行是“严重”格式化的 - 数字(和日期/时间)存储为字符串。如果你运行这个:

var workbook = pck.Workbook;
var worksheet = workbook.Worksheets.First();
var cells = worksheet.Cells;

foreach (var cell in cells)
    Console.WriteLine($"{{Cell: {cell.Address}, Display: {cell.Text}, Value: {cell.Value}, Type: {cell.Value.GetType()}}}");

你在输出中得到这个:

{Cell: A1, Display: 11:33:00 AM, Value: 0.48125, Type: System.Double}
{Cell: A2, Display: 1/1/2016, Value: 1/1/2016 12:00:00 AM, Type: System.DateTime}
{Cell: A3, Display: 1/1/16 11:33 AM, Value: 42370.48125, Type: System.Double}
{Cell: A4, Display: 1264, Value: 1264, Type: System.Double}
{Cell: A5, Display: 11:33:00 AM, Value: 11:33:00 AM, Type: System.String}
{Cell: A6, Display: 1/1/2016, Value: 1/1/2016, Type: System.String}
{Cell: A7, Display: 1/1/2016  11:33:00 AM, Value: 1/1/2016  11:33:00 AM, Type: System.String}
{Cell: A8, Display: 1264, Value: 1264, Type: System.String}

由于日期和时间在技术上只是数字(整数部分是日期而小数是时间),因此这为您提供了转换或分离它们的方法。 0.0的时间倍数表示00:00:00。请注意,第3行显示为System.DateTime,因为就像我说的那样,Epplus恰好认识到Excel样式,但其他人都是Doubles

因此,您可以使用Type检查并避免大部分字符串转换和比较。同样,如果您担心格式错误的数据,那么您的方法可能与任何方法一样好。我建议在for循环之前将值Convert.ToDateTime("12/30/1899"))设置为常量,而不是每次增加时都重新创建它以保存一些cpu循环。