Hi Zhe, Thank you for reproducing the locale-cache issue and confirming its scope and root cause.
I have opened https://github.com/apache/fesod/issues/1065, and I will prepare a fix with regression coverage. Best regards, Minseok Song GitHub: https://github.com/skytin1004 ________________________________ From: ian <[email protected]> Sent: Saturday, 05 September 2026 00:58:28 To: [email protected] <[email protected]> Subject: Re: [VOTE] Release Apache Fesod (Incubating) 2.1.0-incubating RC3 > > > Hi Minseok, > > Thank you for the thorough verification and the excellent analysis — and > thank you for voting +0 with such a clear write-up instead of a silent -1. > > We have reproduced the failure locally and can confirm your findings 100%: > > * Same failing test: DateFormatTest.readCn(ExcelFormat)[1] > (assertion at DateFormatTest.java:60), preceded by > CsvRowTest#csvWrite_withSqlDateAndTime_producesCorrectFile. > * With a Korean JVM default locale the ordered run fails; running > DateFormatTest alone passes. Your diagnosis is exactly right: > NumberDataFormatterUtils keeps a single DataFormatter per thread in a > ThreadLocal and does not re-check (use1904windowing, locale, > useScientificFormat) on subsequent calls. The low-level CSV write path > (CsvSheet#buildCellValue) populates the cache with a formatter bound to > the JVM default locale (CsvWorkbook passes a null locale, which > DataFormatter resolves to Locale.getDefault()) and never clears it, so > a later request for Locale.CHINA on the same thread reuses the > Korean-initialized formatter ("오전" instead of "上午"). > > To answer your specific questions: > > 1. Can this occur during normal application usage? > It is essentially limited to the direct low-level CSV API > (CsvWorkbook/CsvSheet) and to direct use of the public > NumberDataFormatterUtils.format() utility. The managed read/write flows > (FesodSheet.read(...)/write(...)) clear all ThreadLocal caches in > WriteContextImpl.finish() and ExcelAnalyserImpl, including on exception > paths, so two consecutive managed operations with different locales do > not contaminate each other. > > 2. Is the direct CSV path expected to clear this cache? > No — currently it does not, and that is a gap on our side. The low-level > path has no cleanup hook today. > > 3. Does this block the release candidate? > This is a pre-existing, legacy issue in the thread-local formatter > cache, > inherited from the original codebase — it was not introduced by this > release candidate, and RC3 contains no change in this area. That said, > it is a real correctness issue and we very much welcome fixing it: we > intend to track it as a standalone improvement (keying the cached > DataFormatter on its configuration parameters, clearing the cache when > the low-level CSV writer closes, and adding an order-independent > regression test), and contributions from the community are more than > welcome. > > Your verification with NumberDataFormatterUtils.removeThreadLocalCache() > matches our own testing exactly — thank you again for pinning down the root > cause so precisely. It made confirmation and the follow-up work much > easier. > > Best regards, > Zhe Zhang
