diff --git a/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java b/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java index bb949119e9..401b1c47a1 100644 --- a/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java +++ b/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java @@ -70,7 +70,7 @@ public class DateUtil { private static final Pattern date_ptrn2 = Pattern.compile("^\\[[a-zA-Z]+]"); private static final Pattern date_ptrn3a = Pattern.compile("[yYmMdDhHsS]"); // add "\u5e74 \u6708 \u65e5" for Chinese/Japanese date format:2017 \u5e74 2 \u6708 7 \u65e5 - private static final Pattern date_ptrn3b = Pattern.compile("^[\\[\\]yYmMdDhHsS\\-T/\u5e74\u6708\u65e5,. :\"\\\\]+0*[ampAMP/]*$"); + private static final Pattern date_ptrn3b = Pattern.compile("^[\\[\\]yYmMdDhHsS\\-T/\u5e74\u6708\u65e5,. :\"\\\\]+0* ?[ampAMP/]*$"); // elapsed time patterns: [h],[m] and [s] private static final Pattern date_ptrn4 = Pattern.compile("^\\[([hH]+|[mM]+|[sS]+)]"); @@ -548,6 +548,7 @@ public class DateUtil { // avoid re-checking DateUtil.isADateFormat(int, String) if a given format // string represents a date format if the same string is passed multiple times. // see https://issues.apache.org/bugzilla/show_bug.cgi?id=55611 + private static boolean maintainCache = true; private static final ThreadLocal lastFormatIndex = ThreadLocal.withInitial(() -> -1); private static final ThreadLocal lastFormatString = new ThreadLocal<>(); private static final ThreadLocal lastCachedResult = new ThreadLocal<>(); @@ -561,22 +562,24 @@ public class DateUtil { } private static boolean isCached(String formatString, int formatIndex) { - return formatIndex == lastFormatIndex.get() + return maintainCache && formatIndex == lastFormatIndex.get() && formatString.equals(lastFormatString.get()); } private static void cache(String formatString, int formatIndex, boolean cached) { - if (formatString == null || "".equals(formatString)) { - lastFormatString.remove(); - } else { - lastFormatString.set(formatString); + if (maintainCache) { + if (formatString == null || "".equals(formatString)) { + lastFormatString.remove(); + } else { + lastFormatString.set(formatString); + } + if (formatIndex == -1) { + lastFormatIndex.remove(); + } else { + lastFormatIndex.set(formatIndex); + } + lastCachedResult.set(cached); } - if (formatIndex == -1) { - lastFormatIndex.remove(); - } else { - lastFormatIndex.set(formatIndex); - } - lastCachedResult.set(cached); } /** @@ -997,4 +1000,18 @@ public class DateUtil { return tm; } + + /** + * Enable or disable the thread-local cache for date format checking. + * If enabled, the date format checking will be cached per thread, + * which can improve performance when checking the same format multiple times. + * If disabled, the cache will not be used and each check will be performed independently. + * + * @param enable true to enable the cache, false to disable it (enabled, by default) + * @since POI 5.4.2 + */ + public static void enableThreadLocalCache(final boolean enable) { + // enable thread-local cache for date format checking + maintainCache = enable; + } } diff --git a/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java b/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java index 2949dbeeee..61cd1aba69 100644 --- a/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java +++ b/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java @@ -27,6 +27,7 @@ import java.util.TimeZone; import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.model.InternalWorkbook; +import org.apache.poi.ss.usermodel.DataFormatter; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.util.LocaleUtil; import org.junit.jupiter.api.AfterAll; @@ -60,7 +61,7 @@ class TestHSSFDateUtil { HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook("DateFormats.xls"); HSSFSheet sheet = workbook.getSheetAt(0); - InternalWorkbook wb = workbook.getWorkbook(); + InternalWorkbook wb = workbook.getWorkbook(); assertNotNull(wb); HSSFRow row; @@ -115,4 +116,27 @@ class TestHSSFDateUtil { workbook.close(); } + + @Test + void testIsADateFormat() throws IOException { + try (HSSFWorkbook workbook = new HSSFWorkbook()) { + HSSFSheet sheet = workbook.createSheet(); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell(0); + cell.setCellValue(45825.5); // 2025-06-17 (midday) + HSSFCellStyle style = workbook.createCellStyle(); + style.setDataFormat(workbook.createDataFormat().getFormat("DD MMMM, YYYY hh:mm:ss.000 AM/PM")); + cell.setCellStyle(style); + DateUtil.enableThreadLocalCache(false); + try { + assertTrue(DateUtil.isCellDateFormatted(cell), "cell is date formatted?"); + DataFormatter formatter = new DataFormatter(); + String formattedValue = formatter.formatCellValue(cell); + assertEquals("17 June, 2025 12:00:00.000 PM", formattedValue); + } finally { + DateUtil.enableThreadLocalCache(true); + } + } + } + }