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

How to find datetime values in an Epplus ExcelWorksheet

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

我有一个特定列可以为 null 的 DateTime 具有四个可能值的情况:

我提取了一个函数,用于识别工作表中的任何 DateTime 值。虽然 DateTime 值始终被正确识别和格式化,但我不禁认为可能有比嵌套循环更好的方法来实现相同的结果。

因为此函数(以及包含的 class)旨在接受任何 IEnumerable,所以我无法预测哪一列——甚至是否——IEnumerable 将包含 DateTime 值。因此,我无法对已知范围的单元格进行硬编码。

为了提供更好的上下文,这里粘贴了 class 构造函数,后面是我试图改进的函数。即使你去掉注释,它仍然是一组非常丑陋的嵌套代码。

总而言之,我的问题是:在 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;
                    }
                }
            }
        }
    }
}

这归结为您对来源 excel table 的 "properly" 格式有多大信心。我的意思是它们是否存储为正确的日期(即数字),或者您是否有 "numbers stored as string".

的常见 excel 问题

如果数据总体上是干净的,那么您可以通过检查字符串和日期的类型来避免对字符串和日期进行的大量来回转换。即使这也不是完全直接的,因为 Epplus 喜欢在导入日期时进行自己的解释。

看看这个 table(关注 A 列):

第 1 - 4 行具有 "properly" 格式的数据。这意味着日期和时间作为双精度存储在 excel 中。第 5-8 行采用 "badly" 格式 - 数字(和 date/time)存储为字符串。如果你 运行 这个:

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 周期。