excel DateTime.解析日期转换不一致

jpfvwuh4  于 2023-01-21  发布在  其他
关注(0)|答案(1)|浏览(137)

我有一个Excel工作表,其中一列有一个日期。我有C#代码来获取该列,将其转换为日期,然后将其插入SQL数据库。
转换过程如下所示:

r.transactionDate = DateTime.Parse(Convert.ToString(xlRange.Cells[i, 1].Value));

数据传输工作正常,未出现问题。
当Excel工作表中的日期类似于25/05/2022(25 th of May)时,它被正确转换,我在SQL数据库中获得的日期是25/05/2022。
然而,问题是当日期类似于12/05/2022(5月12日)时;将其转换为2022年12月5日(12月5日)。
如何解决此问题?

gojuced7

gojuced71#

这是一张从贝宝下载的excel表格,上面有所有的付款日期,还有一个日期栏。
Paypal不提供XLS或XLSX格式的下载。您所拥有的很可能是Excel可以打开的CSV文件。它在您的文件系统中显示为Excel文件,因为Excel已经注册了CSV文件扩展名。
CSV(逗号分隔值)是一个文本文件,所有值都表示为字符串,由,字符分隔。它已经存在了很长一段时间,但从未正式指定。或者更准确地说,CSV的正式规范已经创建了几次,没有人真正知道哪一个是正确的。CSV文件可以完美地与一个程序运行,但另一个程序可能无法读取。
Excel的CSV导入因使用美国日期格式而不考虑计算机的区域设置而臭名昭著。每个值都被分开并单独检查。如果值看起来像日期格式,则Excel尝试将其解析为美国日期。如果第一组数字在1 - 12范围内,则将其解释为月份。如果第一组数字是13或更大,则它尝试区域日期字符串,如果失败,则可能退回到日期优先(这显然有所不同)。
出于这个原因(以及其他原因),我强烈建议您永远不要通过Excel自动化打开CSV文件。永远不要。事实上,您永远不要使用Excel打开包含日期的CSV文件,除非您知道该文件仅以月份第一天的日期格式生成。即使这样,使用Excel打开文本文件也是不必要的资源浪费。
你可以做得更好。
有很多库可以帮助你导入CSV。我用了一些(CsvHelper是一个合理的起点),但通常我只写一些简单的代码来完成这项工作。(StreamReader.ReadLine),则将每行拆分为值的集合(通过我的SplitCSV方法),然后为该类型编写一个ParseCSV方法,该方法接受一个字符串集合并返回一个配置对象。这样,我就可以直接控制输入数据的解释方式,而不会让Excel把事情搞砸。
下面是一个简单的例子:

const string InputFile = @"C:\Temp\test.csv";

static void Main()
{
    var rows = ReadCSV(InputFile, RowData.ParseCSV);
    foreach (var row in rows)
    {
        Console.WriteLine($"#{row.RowNum}, Date: {row.SomeDate}, Amount: ${row.Amount:#,0.00}");
    }
}

class RowData
{
    public int RowNum { get; set; }
    public DateTime SomeDate { get; set; }
    public decimal Amount { get; set; }
    
    public static RowData ParseCSV(string[] values)
    {
        if (values is null || values.Length < 3)
            return null;
        
        if (!int.TryParse(values[0], out var rownum) || 
            !DateTime.TryParse(values[1], out var somedate) ||
            !decimal.TryParse(values[2], out var amount)
        )
            return null;
        
        return new RowData 
        { 
            RowNum = rownum, 
            SomeDate = somedate, 
            Amount = amount
        };
    }
}

static IEnumerable<T> ReadCSV<T>(string filename, Func<string[], T> parser, bool skipHeaders = true)
    where T : class
{
    bool first = true;
    foreach (var line in Lines(filename))
    {
        if (first)
        {
            first = false;
            if (skipHeaders)
                continue;
        }
        
        T curr = null;
        try
        {
            var values = SplitCSV(line);
            curr = parser(values);
        }
        catch
        {
            // Do something here if you care about bad data.
        }
        
        if (curr != null)
            yield return curr;
    }
}

static string[] SplitCSV(string line)
{
    var sb = new StringBuilder();
    return internal_split().ToArray();
    
    // The actual split, done as an enumerator.
    IEnumerable<string> internal_split()
    {
        bool inQuote = false;
        foreach (char c in line)
        {
            if (c == ',' && !inQuote)
            {
                // yield value
                yield return sb.ToString();
                sb.Clear();
            }
            else if (c == '"')
                inQuote = !inQuote;
            else
                sb.Append(c);
        }
        // yield last field
        yield return sb.ToString();
    }
}

static IEnumerable<string> Lines(string filename)
{
    string line;
    using var reader = File.OpenText(filename);
    while ((line = reader.ReadLine()) != null)
    {
        yield return line;
    }
}

(It这不是最好的方法,但却是一种可行的方法。)

相关问题