From e98274b3c6733e3debf0e119596862f0dd9035f8 Mon Sep 17 00:00:00 2001 From: hanzhe-one <200491821+hanzhe-one@users.noreply.github.com> Date: Mon, 2 Nov 2026 19:32:05 +0800 Subject: [PATCH 1/2] fix: key the date format cache by format index and string --- .../sheet/util/ExcelDateFormatDetector.java | 14 ++++-- .../fesod/sheet/util/DateUtilsTest.java | 43 +++++++++++++++++-- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ExcelDateFormatDetector.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ExcelDateFormatDetector.java index b144e2d43..4ff14f2aa 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ExcelDateFormatDetector.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ExcelDateFormatDetector.java @@ -35,7 +35,7 @@ class ExcelDateFormatDetector { /** * Is a cache of dates */ - private static final ThreadLocal> DATE_THREAD_LOCAL = new ThreadLocal<>(); + private static final ThreadLocal> DATE_THREAD_LOCAL = new ThreadLocal<>(); /** * The following patterns are used in {@link #isADateFormat(Short, String)} @@ -67,21 +67,27 @@ static boolean isADateFormat(Short formatIndex, String formatString) { if (formatIndex == null) { return false; } - Map isDateCache = DATE_THREAD_LOCAL.get(); + // Format indexes are defined per workbook, so the same index can map to different format strings. + final String cacheKey = cacheKey(formatIndex, formatString); + Map isDateCache = DATE_THREAD_LOCAL.get(); if (isDateCache == null) { isDateCache = MapUtils.newHashMap(); DATE_THREAD_LOCAL.set(isDateCache); } else { - Boolean isDatecachedDataList = isDateCache.get(formatIndex); + Boolean isDatecachedDataList = isDateCache.get(cacheKey); if (isDatecachedDataList != null) { return isDatecachedDataList; } } boolean isDate = isADateFormatUncached(formatIndex, formatString); - isDateCache.put(formatIndex, isDate); + isDateCache.put(cacheKey, isDate); return isDate; } + private static String cacheKey(Short formatIndex, String formatString) { + return formatIndex + "\u0000" + (formatString == null ? "" : formatString); + } + /** * Determine if it is a date format. * diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java index c9b43c3e4..6e14aeb50 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java @@ -528,6 +528,7 @@ void test_isADateFormat_false(String formatString) { Assertions.assertFalse(DateUtils.isADateFormat(null, formatString)); } + @SuppressWarnings("unchecked") @Test void test_isADateFormat_Cache() throws NoSuchFieldException, IllegalAccessException { short formatId = 200; @@ -538,15 +539,49 @@ void test_isADateFormat_Cache() throws NoSuchFieldException, IllegalAccessExcept Field threadLocalField = ExcelDateFormatDetector.class.getDeclaredField("DATE_THREAD_LOCAL"); threadLocalField.setAccessible(true); - ThreadLocal> tl = (ThreadLocal>) threadLocalField.get(null); + ThreadLocal> tl = (ThreadLocal>) threadLocalField.get(null); - Map cache = tl.get(); + Map cache = tl.get(); Assertions.assertNotNull(cache); - Assertions.assertTrue(cache.containsKey(formatId)); - Assertions.assertTrue(cache.get(formatId)); + Assertions.assertEquals(1, cache.size()); + Assertions.assertTrue(cache.containsValue(true)); boolean res2 = DateUtils.isADateFormat(formatId, formatStr); Assertions.assertTrue(res2); + Assertions.assertEquals(1, cache.size()); + } + + @Test + void test_isADateFormat_cacheDistinguishesFormatStrings() { + short formatId = 201; + + Assertions.assertTrue(DateUtils.isADateFormat(formatId, "yyyy-mm-dd")); + Assertions.assertFalse(DateUtils.isADateFormat(formatId, "0.00")); + Assertions.assertTrue(DateUtils.isADateFormat(formatId, "yyyy-mm-dd")); + } + + @Test + void test_isADateFormat_cacheDistinguishesFormatStringsInReverseOrder() { + short formatId = 202; + + Assertions.assertFalse(DateUtils.isADateFormat(formatId, "0.00")); + Assertions.assertTrue(DateUtils.isADateFormat(formatId, "yyyy-mm-dd")); + } + + @Test + void test_isADateFormat_cacheDistinguishesNullFormatString() { + short formatId = 203; + + Assertions.assertTrue(DateUtils.isADateFormat(formatId, "yyyy-mm-dd")); + Assertions.assertFalse(DateUtils.isADateFormat(formatId, null)); + } + + @Test + void test_isADateFormat_internalIndexStillOverridesFormatString() { + short internalFormatId = 0x0e; + + Assertions.assertTrue(DateUtils.isADateFormat(internalFormatId, "0.00")); + Assertions.assertFalse(DateUtils.isADateFormat((short) (0x0e + 100), "0.00")); } @Test From dd250c1ce8bb511741448ae9440d46f8e9666aca Mon Sep 17 00:00:00 2001 From: hanzhe-one <200491821+hanzhe-one@users.noreply.github.com> Date: Thu, 5 Nov 2026 20:38:06 +0800 Subject: [PATCH 2/2] test: assert the exact date format cache key --- .../test/java/org/apache/fesod/sheet/util/DateUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java index 6e14aeb50..ccad06474 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java @@ -544,7 +544,7 @@ void test_isADateFormat_Cache() throws NoSuchFieldException, IllegalAccessExcept Map cache = tl.get(); Assertions.assertNotNull(cache); Assertions.assertEquals(1, cache.size()); - Assertions.assertTrue(cache.containsValue(true)); + Assertions.assertEquals(Boolean.TRUE, cache.get(formatId + "\u0000" + formatStr)); boolean res2 = DateUtils.isADateFormat(formatId, formatStr); Assertions.assertTrue(res2);