feat: add java.time.OffsetDateTime converters (#1017) - #1033
Conversation
347d5b5 to
b9122be
Compare
There was a problem hiding this comment.
Pull request overview
Adds built-in OffsetDateTime conversion support for native date, numeric serial, and string Excel cells.
Changes:
- Adds DATE, NUMBER, and STRING converters.
- Registers converters in default loader maps.
- Adds unit tests for formatting, parsing, windowing, and registration.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/OffsetDateTimeConverterTest.java | Updated as part of this pull request. |
| fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/offsetdatetime/OffsetDateTimeStringConverter.java | Updated as part of this pull request. |
| fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/offsetdatetime/OffsetDateTimeNumberConverter.java | Updated as part of this pull request. |
| fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/offsetdatetime/OffsetDateTimeDateConverter.java | Updated as part of this pull request. |
| fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java | Updated as part of this pull request. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { | ||
| format = contentProperty.getDateTimeFormatProperty().getFormat(); | ||
| } | ||
| WorkBookUtil.fillDataFormat(cellData, format, DateUtils.defaultDateFormat); |
There was a problem hiding this comment.
Empty @DateTimeFormat format — Fixed in OffsetDateTimeDateConverter: empty formats are now normalized to null before calling WorkBookUtil.fillDataFormat, so the yyyy-MM-dd HH:mm:ss default is applied. Added a regression test(dateConverterFallsBackToDefaultFormatForEmptyDateTimeFormat).
| if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { | ||
| Boolean propertyUse1904windowing = | ||
| contentProperty.getDateTimeFormatProperty().getUse1904windowing(); | ||
| if (propertyUse1904windowing != null) { | ||
| return propertyUse1904windowing; |
There was a problem hiding this comment.
use1904windowing DEFAULT being unboxed to false — Agreed this is a real issue, but it's a pre-existing framework-level problem: DateTimeFormatProperty.build converts BooleanEnum.DEFAULT (null) to false (DateTimeFormatProperty.java:55-57), and all existing number converters (Date, LocalDate, LocalDateTime, ZonedDateTime) consume the property value without a global fallback. This PR's null-safe fallback covers the no-annotation path; the annotated path behaves identically to the existing converter families. Fixing it properly means changing DateTimeFormatProperty (preserving DEFAULT as null) and updating every date-number converter — a framework-wide change that deserves its own issue/PR. Happy to open one if that's useful.
| String format = format(contentProperty); | ||
| if (StringUtils.isEmpty(format)) { | ||
| return DateTimeFormatter.ISO_OFFSET_DATE_TIME; | ||
| } | ||
| return DateTimeFormatter.ofPattern(format, locale); |
There was a problem hiding this comment.
Formatter caching — Fixed in OffsetDateTimeStringConverter: DateTimeFormatter instances are now cached per pattern and locale in a thread-local map, avoiding rebuilds in the per-cell hot path (the ISO default is a shared constant).
|
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 |
3d00856 to
7dd544e
Compare
Please update the license header. |
|
Thanks for the review. You're right — these files were implemented from scratch, so I've removed the EasyExcel-derived header block (including the Alibaba copyright notice) from the three new converters and the test file. They now carry only the standard ASF header, matching the convention in FesodSheet.java and the LocalTime converters merged in #1032. |
4fb6929 to
255c63a
Compare
e5d642a to
3336675
Compare
|
The tests don't seem to pin the locale? If you swap it for a different locale, nothing fails. Maybe use something locale-sensitive like "dd MMMM yyyy HH:mm:ss XXX"? |
|
2 new methods were added to @Test
void test_parseOffsetDateTime() {
OffsetDateTime expected = OffsetDateTime.of(2020, 1, 2, 3, 4, 5, 0, ZoneOffset.ofHours(8));
Assertions.assertEquals(expected, DateUtils.parseOffsetDateTime("2020-01-02T03:04:05+08:00", null, Locale.US));
Assertions.assertEquals(expected, DateUtils.parseOffsetDateTime("2020-01-02T03:04:05+08:00", "", Locale.US));
Assertions.assertEquals(
expected,
DateUtils.parseOffsetDateTime("02 Januar 2020 03:04:05 +08:00", "dd MMMM yyyy HH:mm:ss XXX", Locale.GERMAN));
Assertions.assertThrows(
DateTimeParseException.class,
() -> DateUtils.parseOffsetDateTime("2020-01-02T03:04:05", null, Locale.US));
}
@Test
void test_format_OffsetDateTime() {
OffsetDateTime value = OffsetDateTime.of(2020, 1, 2, 3, 4, 5, 0, ZoneOffset.ofHours(8));
Assertions.assertNull(DateUtils.format((OffsetDateTime) null, null, Locale.US));
Assertions.assertEquals("2020-01-02T03:04:05+08:00", DateUtils.format(value, null, Locale.US));
Assertions.assertEquals("2020-01-02T03:04:05+08:00", DateUtils.format(value, "", Locale.US));
Assertions.assertEquals(
"02 Januar 2020 03:04:05 +08:00", DateUtils.format(value, "dd MMMM yyyy HH:mm:ss XXX", Locale.GERMAN));
} |
|
Sibling test classes to @AfterEach
void tearDown() {
DateUtils.removeThreadLocalCache();
}Should we also add it for consistency? |
Add OffsetDateTimeStringConverter, OffsetDateTimeNumberConverter and OffsetDateTimeDateConverter, following the existing LocalDateTime and ZonedDateTime converter patterns: - String conversion preserves the offset in ISO-8601 text by default, with a configurable pattern, falling back to local wall-clock time when the offset is missing. - Number and date conversions drop the offset while preserving the local wall-clock time, consistent with the ZonedDateTime converters.
- Read fallback now routes through DateUtils.parseLocalDateTime so the default space-separated format written by other date converters is accepted, and text that does not match a configured pattern is rejected. - Return null instead of NPE for invalid Excel serials, matching the LocalDateTime family. - Null-safe use1904windowing resolution and default-locale fallback.
Address review comments: - OffsetDateTimeDateConverter: an empty @DateTimeFormat value bypassed WorkBookUtil.fillDataFormat's default format (only null falls back), writing an empty/General number format instead of yyyy-MM-dd HH:mm:ss. Normalize empty formats to null; regression test added. - OffsetDateTimeStringConverter: cache DateTimeFormatter instances per pattern and locale in a thread-local map instead of rebuilding them on every cell conversion in the hot path.
…s in OffsetDateTimeConverterTest (apache#1017)
…ers (apache#1017) Addresses second-round review comments on OffsetDateTimeStringConverter. DateUtils now provides parseOffsetDateTime(String, String, Locale) and a format(OffsetDateTime, String, Locale) overload. Both reuse the bounded DATE_TIME_FORMATTER_THREAD_LOCAL cache, which is cleared by removeThreadLocalCache() at the end of each read/write context, instead of a converter-local ThreadLocal that has no cleanup hook. An empty format falls back to ISO_OFFSET_DATE_TIME. Offset-less STRING reads now fail fast with DateTimeParseException instead of silently re-interpreting the text in ZoneId.systemDefault(), which mapped the same text to different instants depending on the server timezone. Tests: the two ZoneId.systemDefault fallback cases now assert failure, the configured-pattern rejection test uses an offset-bearing pattern, and a round-trip case for a configured offset pattern was added.
3336675 to
7653c54
Compare
|
Thanks for the review — all three are addressed in 7653c54 (test-only, no production code changed):
spotless and the full test suite (932 tests) are green. |
|
@bengbengbalabalabeng a quick heads-up on the latest push: 7653c54 only adds tests, covering the three points nkuprins raised — locale-sensitive coverage for the string converter, direct tests for the two new The new commit auto-dismissed your approval, so it needs a fresh approve before it can be merged. Sorry for the extra round trip. |
What and why
Adds a
java.time.OffsetDateTimeconverter family for the OffsetDateTime slice of #1017, following the existingLocalDateTime/ZonedDateTimepattern, so OffsetDateTime fields map to Excel natively instead of falling back toString:OffsetDateTimeDateConverter— write-only, emits an ExcelDATEcell viatoLocalDateTime(), default formatyyyy-MM-dd HH:mm:ssOffsetDateTimeNumberConverter— bidirectionalNUMBERserial, respectsuse1904windowing(property-level first, then a null-safe global default); on read attachesZoneId.systemDefault()to the parsedLocalDateTimeOffsetDateTimeStringConverter— bidirectionalSTRING, honors@DateTimeFormatand the configuredLocale, defaults toISO_OFFSET_DATE_TIMEwhen no format is set; reads parse strictly and reject offset-less text (fail fast instead of a silentZoneId.systemDefault()interpretation)Registered in
DefaultConverterLoader.initAllConverter()/initDefaultWriteConverter().Tests
OffsetDateTimeConverterTestcovers converter keys, DATE/NUMBER/STRING read & write,@DateTimeFormatformatting,use1904windowing(including the null-safe global default) and round-trip behavior. All tests pass,spotless:checkis green, and the full local build was verified.Related: #1017 (OffsetDateTime slice).