项目中我们经常会涉及到这种业务场景:平台用户按照自定义表格模板在前端页面进行导入表格数据,从而进行表单展示,一般表格的导入导出我们会整合POI框架进行开发实现;
导入过程中可能会发现:时间类型的列中,单元格格式为自定义或者时间类型,这种情况下,在后台过程中取到表格的数据,POI框架可能会存在把时间内容转换成国际化时间类型形式或者浮点数形式,再通俗的解释下:比如表格中时间字段内容为:2022-08-16,格式为自定义或时间类型,则后台解析后取到的内容则可能会转换成这样格式:
Tue Aug 16 00:00:00 CST 2022,或者是浮点数44789.0 所以就需要进行数据转换成表格实际的数据内容。
特别说明:POI5.0.0版本在单元格格式为自定义或时间类型时,会解析成国际化时间类型形式,POI3.1.1版本在单元格格式为自定义时,解析成浮点数形式,时间类型则解析成国际化时间类型形式
场景一:后台解析为国际化时间类型形式
一、先构建一个时间转换处理的方法,如下:
/**
*时间格式转换方法,返回字符串类型
*@param val 解析到的时间字段值,类型:字符串 内容:Tue Aug 16 00:00:00 CST 2022
*/
public String getDateOfString(String val){
//定义时间格式变量
SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat dateFormat2 = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyy",Locale.US);
if(val != null){//若表格数据存在时间字段空值,需要做判断,否则会报错
try{
Date date = dateFormat2.parse(val);//用国际化时间类型样式的字符串数据内容来解析转换为date类型 Tue Aug 16 00:00:00 CST 2022
return dateFormat1.format(date); //再将date类型变量按照业务原数据样式转换为字符串形式 2022-08-16 00:00:00
}catch(ParseException e){
e.printStackTrace();
}}
return null;
}
二、后台对应存储对象的时间属性值内容修改
假设我们解析表格的数据,都转换到了集合List<DataVo>dataList当中,需要转换的时间属性为startTime,则调用方法进行属性重新设值,如下:
for(DataVo item:dataList){
//遍历表格(存储到该list集合当中)每一条数据,调用时间转换方法进行时间字段重新赋值
item.setStartTime(getDateOfString(item.getStartTime()));
}
场景二:后台解析为浮点数类型形式
一、先构建一个时间转换处理的方法,如下:
/**
*时间格式转换方法,返回字符串类型
*@param val 解析到的时间字段值,类型:字符串 内容:44789.0
*/
public String getDateOfDouble(String val){
//定义时间格式变量
SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if(val != null){//若表格数据存在时间字段空值,需要做判断,否则会报错
Double value = Double.valueOf(val); //数值类型的字符串val转换为浮点数value
Date date = new Date();
float localOffset = date.getTimezoneOffset()*6000; //获取时区偏移量
date.setTime((long)((value - 25569) * 24 * 3600 + localOffset));//double类型转换成date类型,即44789.0 转换成了date类型格式如: Tue Aug 16 00:00:00 CST 2022
return dateFormat1.format(date); //最后转换成业务原数据的数据格式如:2022-08-16
}
return null;
}
二、后台对应存储对象的时间属性值内容修改
与场景一类似,具体根据业务实际的逻辑程序进行调用即可
这里再拓展一点:有时候业务在刷新表格数据的时候可能没有按照定义好的时间格式来传递,这种是时有发生的,比如原先规定的时间字段格式为:2022-08-16,而数据中可能会有另外一种格式:2022/08/16,这里我个人建议是,业务要尽量按照自己原先规定的格式来补录数据,避免导入失败,当然这前提还是要看时间字段的单元格格式,就像前面所说的,自定义或时间类型的格式,POI就会把数据解析成其他格式,这个时候定义的dateFormat转换样式就需要与实际内容一致,否则解析时就会捕获到异常,那假设单元格格式是常规,则POI不会进行解析,读到是2022-08-16,解析出来也还是2022-08-16,则可以不进行转换。
举例实际场景来验证,如下:
1.表格时间字段单元格格式:自定义/时间类型
2.规定时间格式规范:2022-08-16
3.会存在非规范时间格式数据:2022/08/16
假设表格数据已经初步转换存储到List<DataVo>dataList集合中,但存在部分时间字段的内容与表格的时间内容不一致,需要进行格式转换回原格式,再insert入库。
/**
*时间格式转换后进行数据入库
*@param 保存表格全部数据的list集合
*/
public void importData(List<DataVo> dataList){
//定义时间格式变量
SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat dateFormat2 = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyy",Locale.US);
for(DataVo item : dataList){
try{
Date date = dateFormat2.parse(item.getStartTime());//用国际化时间类型样式的字符串数据内容来解析转换为date类型 Tue Aug 16 00:00:00 CST 2022
String res = dateFormat1.format(date); //再将date类型变量按照业务原数据样式转换为字符串形式 2022-08-16,若当前数据格式是2022/08/16,则会转换失败,跳到catch语句进行格式处理后再赋值给对象数据
item.setStartTime(res);//将转换好的时间格式变量再赋值到对象属性中
}catch(ParseException e){
if(!StringUtils.isEmpty(item.getStartTime())){
item.setStartTime(item.getStartTime().replace('/','-'));将2022/08/16转换成2022-08-16,再赋值给时间属性
}
e.printStackTrace();
}
}
//开始插入数据,建议设置分批导入,避免性能问题
int batchSize = 100; //每批次导入数量
int count = dataList.size() / batchSize;
List<DataVo> temp = null;
for(int i = 0; i < count + 1; ++i){ //++i与i++都是累加,++i性能高
temp = dataList.stream().skip(i * batchSize).limit(batchSize).collect(Collectors.toList());
if (temp.size() > 0){ //判断是否有数据,增强健壮性
dataImportService.insertData(temp); //调用service层对应的insert方法,temp数据传参
}
}
}