基于 WeihanLi.Npoi 实现excel导入时纯汉字的日期转换

Intro

前段时间有位小伙伴在 Github 上提了一个 “不能识别纯汉字的日期格式” issue

二〇二二年一月一日 格式的日期单元格识别不出来会变成,0001/1/1 0:00:00 如何让它能够识别出来呢,基于 InputFormatter 的强大扩展性支持,我们只需要实现一个从纯汉字的日期转换成标准日期的一个转换即可,详细参考后面的实现。.

Implement

首先去网上找了一个将支持将汉字日期转换的方法,实现来自于 http://luoma.pro/Content/Detail/671?parentId=1,(代码有点长,这里只是截出来一部分只是作为示例,有需要的小伙伴也可以自己写一个

// http://luoma.pro/Content/Detail/671?parentId=1
public static class DateTimeUtils
{
    public static bool TransStrToDateTime(string? str, out DateTime dt)
    {
        dt = default;
        if (str.IsNullOrEmpty())
            return false;

        //第一次转换
        if (DateTime.TryParse(str, out dt))
        {
            return true;
        }
        //第二次转换
        string[] format = new string[]
        {
            "yyyyMMdd",
            "yyyyMdHHmmss",
            "yyyyMMddHHmmss",
            "yyyy-M-d",
            "yyyy-MM-dd",
            "yyyy-MM-dd HH:mm:ss",
            "yyyy/M/d",
            "yyyy/MM/dd",
            "yyyy/MM/dd HH:mm:ss",
            "yyyy.M.d",
            "yyyy.MM.dd",
            "yyyy.MM.dd HH:mm:ss",
            "yyyy年M月d日",
            "yyyy年MM月dd日",
            "yyyy年MM月dd日HH:mm:ss",
            "yyyy年MM月dd日 HH时mm分ss秒"
        };
        if (DateTime.TryParseExact(str, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out dt))
        {
            return true;
        }
        //第三次转换
        try
        {
            if (Regex.IsMatch(str, "^(零|〇|一|二|三|四|五|六|七|八|九|十){2,4}年((正|一|二|三|四|五|六|七|八|九|十|十一|十二)月((一|二|三|四|五|六|七|八|九|十){1,3}(日)?)?)?$"))
            {
                var match = Regex.Match(str, @"^(.+)年(.+)月(.+)日$");
                if (match.Success)
                {
                    int year = GetYear(match.Groups[1].Value);
                    int month = GetMonth(match.Groups[2].Value);
                    long dayL = ParseCnToInt(match.Groups[3].Value);
                    dt = new DateTime(year, month, int.Parse(dayL.ToString()));
                    return true;
                }
            }
        }
        catch
        {
            return false;
        }
        return false;
    }
}

接下来来准备一个测试方法和 model:

private sealed class ChineseDateFormatter
{
    public sealed class ChineDateTestModel
    {
        public DateTime Date { get; set; }
    }

    public static DateTime FormatInput(string? input)
    {
        if (DateTimeUtils.TransStrToDateTime(input, out var dt))
        {
            return dt;
        }
        throw new ArgumentException("Invalid date input");
    }
}

最终配置和导入实现代码如下:

FluentSettings.For<ChineseDateFormatter.ChineDateTestModel>()
            .Property(x => x.Date)
            .HasColumnInputFormatter(ChineseDateFormatter.FormatInput);

var excelPath = "<excelPath>"; 
var list = ExcelHelper.ToEntityList<ChineseDateFormatter.ChineDateTestModel>(excelPath);
var item = list[0];
Guard.NotNull(item);
Assert.Equal(DateTime.Parse("2022-01-01"), item.Date);

这里我们导入一个纯汉字的日期来测试一下,excel 内容示例如下:

基于 WeihanLi.Npoi 实现excel导入时纯汉字的日期转换

导入结果如下:

基于 WeihanLi.Npoi 实现excel导入时纯汉字的日期转换

可以看到我们导入的时间此时已经不再是默认值了,可以正常读取出来了

More

其他类似的需求也可以通过这种方式来实现,InputFormatter 主要是针对导入的处理,OutputFormatter 主要针对导出的处理,更多示例可以参考介绍:https://weihanli.github.io/WeihanLi.Npoi/docs/articles/zh/InputOutputFormatter.html(也支持 CSV 的处理)

目前觉得这种纯汉字的日期比较少,我们基本不会用到,也不建议使用,因为大部分库应该都是不支持的基于 WeihanLi.Npoi 实现excel导入时纯汉字的日期转换

所以目前建议自己实现一个 formatter

如果用到的朋友比较多的话,也可以考虑集成在 package 里这样大家使用起来会更方便,欢迎需要的朋友进行反馈。