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..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 @@ -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.assertEquals(Boolean.TRUE, cache.get(formatId + "\u0000" + formatStr)); 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