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

4

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

我有一个特殊列为可空DateTime的情况,它有四种可能的值:

  • null
  • 仅日期
  • 仅时间
  • 日期和时间

我已经提取了一个函数来识别工作表中的任何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个回答

9
这取决于您对源Excel表格的“正确”格式有多自信。我指的是它们是否存储为正确的日期(即数字),或者您是否可能遇到了常见的Excel问题,即“字符串存储为数字”。
如果数据通常很干净,则可以通过检查其类型来避免大部分反复转换字符串和日期的工作。即使这也不完全直截了当,因为Epplus在导入日期时喜欢进行自己的解释。
请看这个表格(重点关注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周期。

非常感谢您提到EPPLus会进行自己的解释。我在处理日期(如1/1/2017)时遇到了麻烦,因为我假设类型是double。我怀疑EPPLus可能正在进行一些预处理,而您的答案证实了这一点。 - Louise Eggleton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接