diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e3286739..775181bd4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
* 【core 】 PathUtil增加isSub和toAbsNormal方法
* 【db 】 RedisDS实现序列化接口(pr#1323@Github)
* 【poi 】 StyleUtil增加getFormat方法(pr#235@Gitee)
+* 【poi 】 增加ExcelDateUtil更多日期格式支持(issue#1316@Github)
### Bug修复
* 【core 】 FileUtil.isSub相对路径判断问题(pr#1315@Github)
diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelDateUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelDateUtil.java
new file mode 100644
index 000000000..fb6349036
--- /dev/null
+++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelDateUtil.java
@@ -0,0 +1,70 @@
+package cn.hutool.poi.excel;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import org.apache.poi.ss.formula.ConditionalFormattingEvaluator;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.ExcelNumberFormat;
+
+/**
+ * Excel中日期判断、读取、处理等补充工具类
+ *
+ * @author looly
+ * @since 5.5.5
+ */
+public class ExcelDateUtil {
+
+ /**
+ * 某些特殊的自定义日期格式
+ */
+ private static final int[] customFormats = new int[]{28, 30, 31, 32, 33, 55, 56, 57, 58};
+
+ public static boolean isDateFormat(Cell cell){
+ return isDateFormat(cell, null);
+ }
+
+ /**
+ * 判断是否日期格式
+ * @param cell 单元格
+ * @param cfEvaluator {@link ConditionalFormattingEvaluator}
+ * @return 是否日期格式
+ */
+ public static boolean isDateFormat(Cell cell, ConditionalFormattingEvaluator cfEvaluator){
+ final ExcelNumberFormat nf = ExcelNumberFormat.from(cell, cfEvaluator);
+ return isDateFormat(nf);
+ }
+
+ /**
+ * 判断是否日期格式
+ * @param numFmt {@link ExcelNumberFormat}
+ * @return 是否日期格式
+ */
+ public static boolean isDateFormat(ExcelNumberFormat numFmt) {
+ return isDateFormat(numFmt.getIdx(), numFmt.getFormat());
+ }
+
+ /**
+ * 判断日期格式
+ *
+ * @param formatIndex 格式索引,一般用于内建格式
+ * @param formatString 格式字符串
+ * @return 是否为日期格式
+ * @since 5.5.3
+ */
+ public static boolean isDateFormat(int formatIndex, String formatString) {
+ // issue#1283@Github
+ if (ArrayUtil.contains(customFormats, formatIndex)) {
+ return true;
+ }
+
+ // 自定义格式判断
+ if (StrUtil.isNotEmpty(formatString) &&
+ StrUtil.containsAny(formatString, "周", "星期", "aa")) {
+ // aa -> 周一
+ // aaa -> 星期一
+ return true;
+ }
+
+ return org.apache.poi.ss.usermodel.DateUtil.isADateFormat(formatIndex, formatString);
+ }
+}
diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java
index 2b71e08aa..390e297f8 100644
--- a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java
+++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java
@@ -3,6 +3,7 @@ package cn.hutool.poi.excel.cell;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
+import cn.hutool.poi.excel.ExcelDateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.StyleSet;
import cn.hutool.poi.excel.editors.TrimEditor;
@@ -102,7 +103,7 @@ public class CellUtil {
if (null == cell) {
return null;
}
- if(cell instanceof NullCell){
+ if (cell instanceof NullCell) {
return null == cellEditor ? null : cellEditor.edit(cell, null);
}
if (null == cellType) {
@@ -111,7 +112,7 @@ public class CellUtil {
// 尝试获取合并单元格,如果是合并单元格,则重新获取单元格类型
final Cell mergedCell = getMergedRegionCell(cell);
- if(mergedCell != cell){
+ if (mergedCell != cell) {
cell = mergedCell;
cellType = cell.getCellTypeEnum();
}
@@ -235,7 +236,7 @@ public class CellUtil {
}
/**
- *获取单元格,如果单元格不存在,返回{@link NullCell}
+ * 获取单元格,如果单元格不存在,返回{@link NullCell}
*
* @param row Excel表的行
* @param cellIndex 列号
@@ -377,7 +378,7 @@ public class CellUtil {
* @since 5.1.5
*/
public static Cell getMergedRegionCell(Cell cell) {
- if(null == cell){
+ if (null == cell) {
return null;
}
return ObjectUtil.defaultIfNull(
@@ -404,10 +405,10 @@ public class CellUtil {
/**
* 为特定单元格添加批注
*
- * @param cell 单元格
- * @param commentText 批注内容
+ * @param cell 单元格
+ * @param commentText 批注内容
* @param commentAuthor 作者
- * @param anchor 批注的位置、大小等信息,null表示使用默认
+ * @param anchor 批注的位置、大小等信息,null表示使用默认
* @since 5.4.8
*/
public static void setComment(Cell cell, String commentText, String commentAuthor, ClientAnchor anchor) {
@@ -431,16 +432,16 @@ public class CellUtil {
// -------------------------------------------------------------------------------------------------------------- Private method start
/**
- * 获取合并单元格,非合并单元格返回null
+ * 获取合并单元格,非合并单元格返回{@code null}
* 传入的x,y坐标(列行数)可以是合并单元格范围内的任意一个单元格
*
* @param sheet {@link Sheet}
* @param x 列号,从0开始,可以是合并单元格范围中的任意一列
* @param y 行号,从0开始,可以是合并单元格范围中的任意一行
- * @return 合并单元格,如果非合并单元格,返回null
+ * @return 合并单元格,如果非合并单元格,返回{@code null}
* @since 5.4.5
*/
- private static Cell getCellIfMergedRegion(Sheet sheet, int x, int y){
+ private static Cell getCellIfMergedRegion(Sheet sheet, int x, int y) {
final int sheetMergeCount = sheet.getNumMergedRegions();
CellRangeAddress ca;
for (int i = 0; i < sheetMergeCount; i++) {
@@ -463,9 +464,8 @@ public class CellUtil {
final CellStyle style = cell.getCellStyle();
if (null != style) {
- final short formatIndex = style.getDataFormat();
// 判断是否为日期
- if (isDateType(cell, formatIndex)) {
+ if (ExcelDateUtil.isDateFormat(cell)) {
return DateUtil.date(cell.getDateCellValue());// 使用Hutool的DateTime包装
}
@@ -483,32 +483,5 @@ public class CellUtil {
// 某些Excel单元格值为double计算结果,可能导致精度问题,通过转换解决精度问题。
return Double.parseDouble(NumberToTextConverter.toText(value));
}
-
- /**
- * 是否为日期格式
- * 判断方式:
- *
- *
- * 1、指定序号 - * 2、org.apache.poi.ss.usermodel.DateUtil.isADateFormat方法判定 - *- * - * @param cell 单元格 - * @param formatIndex 格式序号 - * @return 是否为日期格式 - */ - private static boolean isDateType(Cell cell, int formatIndex) { - // yyyy-MM-dd----- 14 - // yyyy年m月d日---- 31 - // yyyy年m月------- 57 - // m月d日 ---------- 58 - // HH:mm----------- 20 - // h时mm分 -------- 32 - if (formatIndex == 14 || formatIndex == 31 || formatIndex == 57 || formatIndex == 58 || formatIndex == 20 || formatIndex == 32) { - return true; - } - - return org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell); - } // -------------------------------------------------------------------------------------------------------------- Private method end } diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/ExcelSaxUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/ExcelSaxUtil.java index 5f50f9c12..e92095674 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/ExcelSaxUtil.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/ExcelSaxUtil.java @@ -6,6 +6,7 @@ import cn.hutool.core.exceptions.DependencyException; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.StrUtil; +import cn.hutool.poi.excel.ExcelDateUtil; import cn.hutool.poi.excel.sax.handler.RowHandler; import cn.hutool.poi.exceptions.POIException; import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; @@ -209,16 +210,10 @@ public class ExcelSaxUtil { * @param formatString 格式字符串 * @return 是否为日期格式 * @since 5.5.3 + * @see ExcelDateUtil#isDateFormat(int, String) */ public static boolean isDateFormat(int formatIndex, String formatString) { - // https://blog.csdn.net/u014342130/article/details/50619503 - // issue#1283@Github - if (formatIndex == 28 || formatIndex == 31) { - // 28 -> m月d日 - // 31 -> yyyy年m月d日 - return true; - } - return org.apache.poi.ss.usermodel.DateUtil.isADateFormat(formatIndex, formatString); + return ExcelDateUtil.isDateFormat(formatIndex, formatString); } /** diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelSaxReadTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelSaxReadTest.java index 4d5ef39f8..785eb2899 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelSaxReadTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/ExcelSaxReadTest.java @@ -164,6 +164,14 @@ public class ExcelSaxReadTest { Assert.assertEquals("2012-12-21 00:00:00", rows.get(4)); } + @Test + @Ignore + public void dateReadXlsxTest2() { + ExcelUtil.readBySax("d:/test/custom_date_format2.xlsx", 0, + (i, i1, list) -> Console.log(list) + ); + } + @Test @Ignore public void readBlankTest() {