[bug-69681] allow 1 optional space in date formats before the AM/PM part

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1926508 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
PJ Fanning 2025-06-17 17:18:12 +00:00
parent 779358f309
commit c784266438
2 changed files with 54 additions and 13 deletions

View File

@ -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 (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;
}
}

View File

@ -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);
}
}
}
}