Author: fanningpj
Date: Tue Jun 17 17:18:12 2025
New Revision: 1926508

URL: http://svn.apache.org/viewvc?rev=1926508&view=rev
Log:
[bug-69681] allow 1 optional space in date formats before the AM/PM part

Modified:
    poi/trunk/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java
    
poi/trunk/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java

Modified: poi/trunk/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java?rev=1926508&r1=1926507&r2=1926508&view=diff
==============================================================================
--- poi/trunk/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java 
[UTF-8] (original)
+++ poi/trunk/poi/src/main/java/org/apache/poi/ss/usermodel/DateUtil.java 
[UTF-8] Tue Jun 17 17:18:12 2025
@@ -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<Integer> lastFormatIndex = 
ThreadLocal.withInitial(() -> -1);
     private static final ThreadLocal<String> lastFormatString = new 
ThreadLocal<>();
     private static final ThreadLocal<Boolean> 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 (formatIndex == -1) {
-            lastFormatIndex.remove();
-        } else {
-            lastFormatIndex.set(formatIndex);
+        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);
         }
-        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;
+    }
 }

Modified: 
poi/trunk/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java?rev=1926508&r1=1926507&r2=1926508&view=diff
==============================================================================
--- 
poi/trunk/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java 
(original)
+++ 
poi/trunk/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFDateUtil.java 
Tue Jun 17 17:18:12 2025
@@ -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);
+            }
+        }
+    }
+
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to