feat: add ZonedDateTime converters - #1020
Conversation
There was a problem hiding this comment.
Pull request overview
Adds first-class java.time.ZonedDateTime converter support to the fesod-sheet module, integrating it into the default converter registry and providing unit coverage to validate the expected zone/offset handling behavior.
Changes:
- Introduces ZonedDateTime converters for STRING, NUMBER, and DATE write scenarios.
- Registers the new converters in
DefaultConverterLoaderfor default read/write discovery. - Adds unit tests to validate conversion behavior and default registration.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java | Registers ZonedDateTime converters in the default loader maps. |
| fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/zoneddatetime/ZonedDateTimeDateConverter.java | Adds DATE write converter (drops zone via toLocalDateTime() and applies data format). |
| fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/zoneddatetime/ZonedDateTimeNumberConverter.java | Adds NUMBER read/write converter using Excel serial dates and ZoneId.systemDefault() on read. |
| fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/zoneddatetime/ZonedDateTimeStringConverter.java | Adds STRING read/write converter with ISO/custom pattern formatting and parsing fallback. |
| fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/ZonedDateTimeConverterTest.java | Adds targeted tests for conversion semantics and loader registration. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
nkuprins
left a comment
There was a problem hiding this comment.
Consider adding a test for use1904windowing :)
|
Added focused |
|
The newly added files in this PR are implemented from scratch and are not derived from Alibaba's EasyExcel. Therefore, no EasyExcel-related license header is required for these files. Please refer to: https://github.com/apache/fesod/blob/main/fesod-sheet/src/main/java/org/apache/fesod/sheet/FesodSheet.java |
| WriteCellData<?> cellData = new WriteCellData<>(localDateTime); | ||
| String format = null; | ||
| if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { | ||
| format = contentProperty.getDateTimeFormatProperty().getFormat(); |
There was a problem hiding this comment.
Empty @DateTimeFormat values are passed through to WorkBookUtil.fillDataFormat as-is. fillDataFormat only substitutes the default format when the argument is null — an empty string ("") is written into the cell style as-is, so @DateTimeFormat(value = "") produces a DATE cell with no format at all instead of falling back to yyyy-MM-dd HH:mm:ss.
Suggest normalizing the empty format to null before the call:
String format = null;
if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) {
format = contentProperty.getDateTimeFormatProperty().getFormat();
if (StringUtils.isEmpty(format)) {
format = null;
}
}
WorkBookUtil.fillDataFormat(cellData, format, DateUtils.defaultDateFormat);This also keeps the DATE direction consistent with the string converter below, which already falls back when the format is empty.
There was a problem hiding this comment.
Fixed in f882ea6: empty DATE formats are normalized to null before fillDataFormat, so the existing yyyy-MM-dd HH:mm:ss default is applied. Added a regression test.
| || contentProperty.getDateTimeFormatProperty() == null | ||
| || StringUtils.isEmpty( | ||
| contentProperty.getDateTimeFormatProperty().getFormat())) { | ||
| return DateTimeFormatter.ISO_ZONED_DATE_TIME; |
There was a problem hiding this comment.
DateTimeFormatter.ofPattern(...) is invoked for every cell conversion — once in convertToJavaData and once in convertToExcelData. In the per-cell hot path (large sheets) this rebuilds the same formatter over and over, since the pattern and locale change only per property, not per cell.
Suggest caching formatters per (pattern, locale), e.g. a ThreadLocal map with the ISO default kept as a shared constant:
private static final ThreadLocal<Map<String, DateTimeFormatter>> FORMATTER_CACHE = new ThreadLocal<>();There was a problem hiding this comment.
Addressed in f882ea6 by reusing the existing bounded per-thread locale/pattern formatter cache in DateUtils for configured patterns. No new converter-local ThreadLocal was introduced; ISO remains the shared constant.
| return DateTimeFormatter.ofPattern( | ||
| contentProperty.getDateTimeFormatProperty().getFormat(), locale); | ||
| } | ||
| } |
There was a problem hiding this comment.
Empty @DateTimeFormat values are handled inconsistently across the three converters:
ZonedDateTimeStringConverter(line 81) falls back toISO_ZONED_DATE_TIME→2020-01-02T03:04:05ZZonedDateTimeDateConverter(line 54) passes""through tofillDataFormat→ cell ends up with no format at allZonedDateTimeNumberConverter→ the cell is formatted with the workbook default
So the same @DateTimeFormat(value = "") produces three different outputs depending on the cell type. Suggest picking one behavior — falling back to the default yyyy-MM-dd HH:mm:ss on all three keeps it consistent with the other date-time families.
There was a problem hiding this comment.
The DATE defect is fixed in f882ea6. I left the three representations defaults intentionally distinct: STRING uses ISO_ZONED_DATE_TIME to preserve zone and offset text, while DATE and NUMBER use Excel date semantics and the workbook default. This matches the existing converter-family conventions, so a blanket yyyy-MM-dd HH:mm:ss fallback would change intended STRING behavior.
|
Thanks for addressing all three points — verified locally, all tests green (8/8). One heads-up: the PR is currently in a CONFLICTING state ( |
…mpty format pattern - Add @tag(Tags.UNIT) to ZonedDateTimeConverterTest following repository conventions - Handle empty or null format strings in ZonedDateTimeStringConverter by falling back to ISO_ZONED_DATE_TIME - Add regression coverage for empty and null format patterns
f882ea6 to
0c342db
Compare
Purpose of the pull request
Related: #1017
What's changed?
Adds the approved
java.time.ZonedDateTimeconverter family and registers it with the default converter loader.toLocalDateTime(), intentionally dropping zone/offset while preserving local wall-clock fields.ZoneId.systemDefault().ZonedDateTimeConverterTestcoverage for supported directions, registration, formatting, and timezone-lossiness behavior.Checklist
Focused validation: 6 ZonedDateTime tests passed; Java 1.8-targeted compilation, Spotless, and
git diff --checkpassed.