POI识别Excel表格的中文时间和日期格式单元格

POI识别Excel表格的中文时间和日期格式单元格

本文的重点是解决两个问题:

1、POI识别Excel的时间包含中文,例如:2021年1月22日

2、POI识别Excel中单元格为日期类型或时间类型的时间数据

前言:

本文主要以示例解决以上两个问题,为了突出主要解决问题的代码,所以就以xlsx类型表格的识别方式为例。

一、POI识别包含中文的时间

前言:
主要以 xxxx年xx月xx日 类型时间为例进行演示

背景:
自POI 3.X版本开始,poi便无法识别Excel中的中文时间了。然而,当我们的Excel中出现中文的时候,我们仍然是要对其进行识别的。

分析:

poi中对于数据类型的识别,主要用到的是org.apache.poi.ss.usermodel.DateUtil包中的DateUtil.isCellDateFormatted(Cell cell) 方法。通过查看方法isCellDateFormatted的源码,我们发现,其中关于对时间数据进行分析的方法 isADateFormat 仅对符号进行了分析,没有对相对应的中文数据进行分析,例如‘年’、‘月’、‘日’等关键的中文字符。

//以下 isADateFormat 方法中对于时间数据的分析源码
if (separatorIndex < length - 1) {
	char nc = fs.charAt(separatorIndex + 1);
	if (c == '\\') {
        switch(nc) {
            case ' ':
            case ',':
            case '-':
            case '.':
            case '\\':
            continue;
        }
    } else if (c == ';' && nc == '@') {
    	++separatorIndex;
    	continue;
	}
}

解决方案:

为了实现POI中的 isCellDateFormatted 能够对中文时间进行解析,我们需要重写一下该方法,下列是修改后的方法,可直接复制使用

//直接创建一个DateFormatUtil的工具类,当我们需要对时间类型进行判断时,直接使用DateFormatUtil.isCellDateFormatted (Cell cell)方法即可
public class DateFormatUtil{
    public static boolean isCellDateFormatted(Cell cell)
    {
        if (cell == null) {
            return false;
        }
        boolean bDate = false;

        double d = cell.getNumericCellValue();
        if (isValidExcelDate(d)) {
            CellStyle style = cell.getCellStyle();
            if (style == null) {
                return false;
            }
            int i = style.getDataFormat();
            String f = style.getDataFormatString();
            bDate = isADateFormat(i, f);
        }
        return bDate;
    }

    public static boolean isADateFormat(int formatIndex, String formatString)
    {
        if (isInternalDateFormat(formatIndex)) {
            return true;
        }

        if ((formatString == null) || (formatString.length() == 0)) {
            return false;
        }

        String fs = formatString;
        //下面这一行是自己手动添加的 以支持汉字格式wingzing
        fs = fs.replaceAll("[\"|\']","").replaceAll("[年|月|日|时|分|秒|毫秒|微秒]", "");
        fs = fs.replaceAll("\\\\-", "-");
        fs = fs.replaceAll("\\\\,", ",");
        fs = fs.replaceAll("\\\\.", ".");
        fs = fs.replaceAll("\\\\ ", " ");
        fs = fs.replaceAll(";@", "");
        fs = fs.replaceAll("^\\[\\$\\-.*?\\]", "");
        fs = fs.replaceAll("^\\[[a-zA-Z]+\\]", "");
        return (fs.matches("^[yYmMdDhHsS\\-/,. :]+[ampAMP/]*$"));
    }

    public static boolean isInternalDateFormat(int format)
    {
        switch (format) { case 14:
            case 15:
            case 16:
            case 17:
            case 18:
            case 19:
            case 20:
            case 21:
            case 22:
            case 45:
            case 46:
            case 47:
            case 57:
            case 58:
                return true;
            case 23:
            case 24:
            case 25:
            case 26:
            case 27:
            case 28:
            case 29:
            case 30:
            case 31:
            case 32:
            case 33:
            case 34:
            case 35:
            case 36:
            case 37:
            case 38:
            case 39:
            case 40:
            case 41:
            case 42:
            case 43:
            case 44: } return false;
    }

    public static boolean isValidExcelDate(double value)
    {
        return (value > -4.940656458412465E-324D);
    }
}

二、POI识别Excel中单元格为日期类型或时间类型的时间数据

2.1 单元格类型

讲解POI识别Excel表格中时间数据的问题前,我们先要了解一下,就是Excel表格中,关于时间数据的单元格共有几种定义方式。

废话不多说,直接上图:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

如图所示,在Excel表格中共有三种表示时间的单元格类型,分别为日期类型、时间类型和自定义类型。

2.2 自定义类型单元格–时间处理

在上面三种单元格类型中,自定义类型单元格存放的时间数据,可以直接通过getDataFormat()值来判断,常用的时间类型及其对应的format值如下:

时间类型 format值
yyyy-MM-dd 14
yyyy年m月d日 31
yyyy年m月 57
m月d日 58
HH:mm 20
h时mm分 32

下面是我对几种常见时间类型进行处理的代码

//先获取对应的format值
short format = row.getCell(j).getCellStyle().getDataFormat();
SimpleDateFormat sdf = null;
//根据format值,将其转换成对应的时间样式
if (format == 20 || format == 32){
	sdf = new SimpleDateFormat("HH:mm");
}else if (format == 14 || format == 31 || format == 57 || format == 58){
	sdf = new SimpleDateFormat("yyyy-MM-dd");
}else {
	sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}

2.3 日期或时间类型单元格–判断和处理

分析:

与自定义类型单元格的时间有所区别,日期类型和时间类型单元格的时间数据,我们无法通过format值对其进行判断。所以,在如上的代码中,会出现将“yyyy-MM-dd”和“HH:mm”类型的时间,直接转换成"yyyy-MM-dd HH:mm:ss"格式,既不美观也不符合我们对其格式的需求。

解决方案:(以下的解决方案仅为我个人的处理方法,可能不严谨,仅供参考)
步骤:

  1. 先将数据转换成 "yyyy-MM-dd HH:mm:ss"的格式
  2. 将SimpleDateFormat类型的时间转换成String类型,并去除其中的空格、:和-三个符号,存进String类型数组中
  3. 通过数组进行中时分秒的数据进行判断最终要展示的时间样式,例:判断小时为0时,则不显示时分秒;判断秒钟为0时,则不显示年月日

直接上代码:

//先获取对应的format值
short format = row.getCell(j).getCellStyle().getDataFormat();
SimpleDateFormat sdf = null;
//根据format值,将其转换成对应的时间样式
if (format == 20 || format == 32){
	sdf = new SimpleDateFormat("HH:mm");
}else if (format == 14 || format == 31 || format == 57 || format == 58){
	sdf = new SimpleDateFormat("yyyy-MM-dd");
}else {
    /**
    * 上面处理的时间,单元格格式都是自定义格式中时间格式,而下面解决的是单元格格式为日期格式的问题
    * 单元格格式为日期格式时,没办法通过format进行判断,所以进行如下操作:
    * 1、先将数据转换成 "yyyy-MM-dd HH:mm:ss"的格式
    * 2、将SimpleDateFormat类型的时间转换成String类型,并去除其中的空格、:和-三个符号,存进String类型数组中
    * 3、通过数组进行中时分秒的数据进行判断最终要展示的时间样式
    * 判断小时为0时,则不显示时分秒;判断秒钟为0时,则不显示年月日
    */
    sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    // split方法去除多个符号,使用 | 分隔符进行简单的配置
    // 转化为String类型数组后,我们数组中存储的数据依次为0-年、1-月、2-日、3-时、4-分、5-秒(数组下标-存放的数据)
    String[] str = sdf.format(DateUtil.getJavaDate(row.getCell(j).getNumericCellValue())).toString().split("-|:| ");
    if (Integer.parseInt(str[3]) == 0 && Integer.parseInt(str[4]) == 0 && Integer.parseInt(str[5]) == 0){
        //根据表格,当时分秒都为0时,说明此单元格存储的数据为“yyyy/MM/dd”,此处只显示年月日
        sdf = new SimpleDateFormat("yyyy-MM-dd");
	}else if (Integer.parseInt(str[3]) != 0 && Integer.parseInt(str[4]) != 0 && Integer.parseInt(str[5]) == 0){
        //根据表格,当小时和分钟不为0的情况,说明此单元格存储的数据为“HH:mm”,年月日是不需要的
        sdf = new SimpleDateFormat("HH:mm");
	}else{
		break;
	}
}

该解释都在代码中解释了,代码中例子的范围有限,仅供参考,实际还是需要根据自己的数据进行分析。

完整代码:

最后附上我识别xlsx类型表格的完整代码

//  1.根据传入的文件路径获取工作簿
XSSFWorkbook xssfworkbook = new XSSFWorkbook(path);

//  2.根据脚标获取工作表,index从0开始
XSSFSheet sheet = xssfworkbook.getSheetAt(1);
int lastRowNum = sheet.getLastRowNum();
for (int i = 2;i <= lastRowNum; i++) {
    XSSFRow row = sheet.getRow(i);

    if (row != null){
        List<String> list =new ArrayList();
        for (int j = 0; j < 13;j++){
            if (row.getCell(j) != null || !row.getCell(j).toString().trim().equals("")) {
                String value = null;
                //判断获取到的数据是否为数字类型数据
                if (row.getCell(j).getCellType() == CellType.NUMERIC){
                    //是数字类型数据,进行类型转换
                    //获取日期类型
                    short format = row.getCell(j).getCellStyle().getDataFormat();
                    //此处运用了重写的isCellDateFormatted方法,在DateFormatUtil工具类中
                    if (DateFormatUtil.isCellDateFormatted(row.getCell(j))){
                        SimpleDateFormat sdf =null;
                        if (format == 20 || format == 32){
                        	sdf = new SimpleDateFormat("HH:mm");
                        }else if (format == 14 || format == 31 || format == 57 || format == 58){
                        	sdf = new SimpleDateFormat("yyyy-MM-dd");
                        }else {
                            /**
                            * 上面处理的时间,单元格格式都是自定义格式中时间格式,而下面解决的是单元格格式为日期格式的问题
                            * 单元格格式为日期格式时,没办法通过format进行判断,所以进行如下操作:
                            * 1、现将数据转换成 "yyyy-MM-dd HH:mm:ss"的格式
                            * 2、将SimpleDateFormat类型的时间转换成String类型,并去除其中的空格、:和-三个符号,存进String类型数组中
                            * 3、通过数组进行中时分秒的数据进行判断最终要展示的时间样式
                            * 判断小时为0时,则不显示时分秒;判断秒钟为0时,则不显示年月日
                            */
                            sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            String[] str = sdf.format(DateUtil.getJavaDate(row.getCell(j).getNumericCellValue())).toString().split("-|:| ");
                        	if (Integer.parseInt(str[3]) == 0 && Integer.parseInt(str[4]) == 0 && Integer.parseInt(str[5]) == 0){
                        	//根据表格,当时分秒都为0时,说明此单元格为生产日期,此处只显示年月日
                        	sdf = new SimpleDateFormat("yyyy-MM-dd");
                            }else if (Integer.parseInt(str[3]) != 0 && Integer.parseInt(str[4]) != 0 && Integer.parseInt(str[5]) == 0){
                                //根据表格,当小时和分钟不为0的情况,说明此单元格为初凝和终凝,年月日是不需要的
                                sdf = new SimpleDateFormat("HH:mm");
                            }else{
                                break;
                        	}
                        }
                        double value1 = row.getCell(j).getNumericCellValue();
                        Date date = DateUtil.getJavaDate(value1);
                        value = sdf.format(date);
                    }else{
                   		value = String.valueOf(row.getCell(j));
                    }
                }else {
                	value = String.valueOf(row.getCell(j));
                }
                list.add(value);
            }
        }
        //使用for循环list中的数据存储到对应的bean对象中
    }
}
//最后根据自己方法体中需要返回的数据进行封装并返回即可

猜你喜欢

转载自blog.csdn.net/xiri_/article/details/112986449
今日推荐