From e7ee438992b7baa595cc212f2feee12d06decafc Mon Sep 17 00:00:00 2001 From: Syed Ishmum Ahnaf Date: Tue, 18 Aug 2026 01:05:56 +0600 Subject: [PATCH 1/5] feat: add java.sql.Timestamp converters --- .../converters/DefaultConverterLoader.java | 7 + .../timestamp/TimestampDateConverter.java | 59 +++++++ .../timestamp/TimestampNumberConverter.java | 80 +++++++++ .../timestamp/TimestampStringConverter.java | 82 +++++++++ .../converter/TimestampConverterTest.java | 164 ++++++++++++++++++ 5 files changed, 392 insertions(+) create mode 100644 fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java create mode 100644 fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java create mode 100644 fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampStringConverter.java create mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java index ccc8a9f6a..95a247442 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/DefaultConverterLoader.java @@ -85,6 +85,9 @@ import org.apache.fesod.sheet.converters.string.StringErrorConverter; import org.apache.fesod.sheet.converters.string.StringNumberConverter; import org.apache.fesod.sheet.converters.string.StringStringConverter; +import org.apache.fesod.sheet.converters.timestamp.TimestampDateConverter; +import org.apache.fesod.sheet.converters.timestamp.TimestampNumberConverter; +import org.apache.fesod.sheet.converters.timestamp.TimestampStringConverter; import org.apache.fesod.sheet.converters.url.UrlImageConverter; import org.apache.fesod.sheet.converters.uuid.UUIDStringConverter; import org.apache.fesod.sheet.converters.year.YearStringConverter; @@ -136,6 +139,8 @@ private static void initAllConverter() { putAllConverter(new LocalTimeStringConverter()); putAllConverter(new SqlTimeNumberConverter()); putAllConverter(new SqlTimeStringConverter()); + putAllConverter(new TimestampNumberConverter()); + putAllConverter(new TimestampStringConverter()); putAllConverter(new OffsetDateTimeNumberConverter()); putAllConverter(new OffsetDateTimeStringConverter()); @@ -185,6 +190,7 @@ private static void initDefaultWriteConverter() { putWriteConverter(new LocalTimeDateConverter()); putWriteConverter(new InstantStringConverter()); putWriteConverter(new SqlTimeDateConverter()); + putWriteConverter(new TimestampDateConverter()); putWriteConverter(new OffsetDateTimeDateConverter()); putWriteConverter(new DoubleNumberConverter()); putWriteConverter(new FloatNumberConverter()); @@ -212,6 +218,7 @@ private static void initDefaultWriteConverter() { putWriteStringConverter(new LocalTimeStringConverter()); putWriteStringConverter(new InstantStringConverter()); putWriteStringConverter(new SqlTimeStringConverter()); + putWriteStringConverter(new TimestampStringConverter()); putWriteStringConverter(new OffsetDateTimeStringConverter()); putWriteStringConverter(new DoubleStringConverter()); putWriteStringConverter(new FloatStringConverter()); diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java new file mode 100644 index 000000000..f855033cc --- /dev/null +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. + * + * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. + */ + +package org.apache.fesod.sheet.converters.timestamp; + +import java.sql.Timestamp; +import org.apache.fesod.sheet.converters.Converter; +import org.apache.fesod.sheet.metadata.GlobalConfiguration; +import org.apache.fesod.sheet.metadata.data.WriteCellData; +import org.apache.fesod.sheet.metadata.property.ExcelContentProperty; +import org.apache.fesod.sheet.util.DateUtils; +import org.apache.fesod.sheet.util.WorkBookUtil; + +/** + * Timestamp and date converter + * + * + */ +public class TimestampDateConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return Timestamp.class; + } + + @Override + public WriteCellData convertToExcelData( + Timestamp value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) + throws Exception { + WriteCellData cellData = new WriteCellData<>(value); + String format = null; + if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { + format = contentProperty.getDateTimeFormatProperty().getFormat(); + } + WorkBookUtil.fillDataFormat(cellData, format, DateUtils.defaultDateFormat); + return cellData; + } +} diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java new file mode 100644 index 000000000..bd7bf8686 --- /dev/null +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. + * + * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. + */ + +package org.apache.fesod.sheet.converters.timestamp; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import org.apache.fesod.sheet.converters.Converter; +import org.apache.fesod.sheet.enums.CellDataTypeEnum; +import org.apache.fesod.sheet.metadata.GlobalConfiguration; +import org.apache.fesod.sheet.metadata.data.ReadCellData; +import org.apache.fesod.sheet.metadata.data.WriteCellData; +import org.apache.fesod.sheet.metadata.property.ExcelContentProperty; +import org.apache.fesod.sheet.util.DateUtils; +import org.apache.poi.ss.usermodel.DateUtil; + +/** + * Timestamp and number converter + * + * + */ +public class TimestampNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Timestamp.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public Timestamp convertToJavaData( + ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return Timestamp.valueOf(DateUtils.getLocalDateTime( + cellData.getNumberValue().doubleValue(), globalConfiguration.getUse1904windowing())); + } else { + return Timestamp.valueOf(DateUtils.getLocalDateTime( + cellData.getNumberValue().doubleValue(), + contentProperty.getDateTimeFormatProperty().getUse1904windowing())); + } + } + + @Override + public WriteCellData convertToExcelData( + Timestamp value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return new WriteCellData<>( + BigDecimal.valueOf(DateUtil.getExcelDate(value, globalConfiguration.getUse1904windowing()))); + } else { + return new WriteCellData<>(BigDecimal.valueOf(DateUtil.getExcelDate( + value, contentProperty.getDateTimeFormatProperty().getUse1904windowing()))); + } + } +} diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampStringConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampStringConverter.java new file mode 100644 index 000000000..d5dc7efa9 --- /dev/null +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampStringConverter.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. + * + * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. + */ + +package org.apache.fesod.sheet.converters.timestamp; + +import java.sql.Timestamp; +import java.text.ParseException; +import org.apache.fesod.sheet.converters.Converter; +import org.apache.fesod.sheet.enums.CellDataTypeEnum; +import org.apache.fesod.sheet.metadata.GlobalConfiguration; +import org.apache.fesod.sheet.metadata.data.ReadCellData; +import org.apache.fesod.sheet.metadata.data.WriteCellData; +import org.apache.fesod.sheet.metadata.property.ExcelContentProperty; +import org.apache.fesod.sheet.util.DateUtils; + +/** + * Timestamp and string converter + * + * + */ +public class TimestampStringConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return Timestamp.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Timestamp convertToJavaData( + ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) + throws ParseException { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return Timestamp.valueOf( + DateUtils.parseLocalDateTime(cellData.getStringValue(), null, globalConfiguration.getLocale())); + } else { + return Timestamp.valueOf(DateUtils.parseLocalDateTime( + cellData.getStringValue(), + contentProperty.getDateTimeFormatProperty().getFormat(), + globalConfiguration.getLocale())); + } + } + + @Override + public WriteCellData convertToExcelData( + Timestamp value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return new WriteCellData<>( + DateUtils.format(value.toLocalDateTime(), null, globalConfiguration.getLocale())); + } else { + return new WriteCellData<>(DateUtils.format( + value.toLocalDateTime(), + contentProperty.getDateTimeFormatProperty().getFormat(), + globalConfiguration.getLocale())); + } + } +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java new file mode 100644 index 000000000..f83c50859 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. + * + * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. + */ + +package org.apache.fesod.sheet.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.Locale; +import org.apache.fesod.sheet.converters.ConverterKeyBuild; +import org.apache.fesod.sheet.converters.DefaultConverterLoader; +import org.apache.fesod.sheet.converters.timestamp.TimestampDateConverter; +import org.apache.fesod.sheet.converters.timestamp.TimestampNumberConverter; +import org.apache.fesod.sheet.converters.timestamp.TimestampStringConverter; +import org.apache.fesod.sheet.enums.CellDataTypeEnum; +import org.apache.fesod.sheet.metadata.GlobalConfiguration; +import org.apache.fesod.sheet.metadata.data.ReadCellData; +import org.apache.fesod.sheet.metadata.data.WriteCellData; +import org.apache.fesod.sheet.metadata.property.DateTimeFormatProperty; +import org.apache.fesod.sheet.metadata.property.ExcelContentProperty; +import org.apache.fesod.sheet.testkit.Tags; +import org.apache.fesod.sheet.util.DateUtils; +import org.apache.poi.ss.usermodel.DateUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for java.sql.Timestamp converter implementations. + */ +@Tag(Tags.UNIT) +public class TimestampConverterTest { + + private static final GlobalConfiguration GLOBAL_CONFIGURATION = new GlobalConfiguration(); + + @Test + void supportKeys() { + assertEquals(Timestamp.class, new TimestampDateConverter().supportJavaTypeKey()); + + TimestampNumberConverter numberConverter = new TimestampNumberConverter(); + assertEquals(Timestamp.class, numberConverter.supportJavaTypeKey()); + assertEquals(CellDataTypeEnum.NUMBER, numberConverter.supportExcelTypeKey()); + + TimestampStringConverter stringConverter = new TimestampStringConverter(); + assertEquals(Timestamp.class, stringConverter.supportJavaTypeKey()); + assertEquals(CellDataTypeEnum.STRING, stringConverter.supportExcelTypeKey()); + } + + @Test + void numberConverterReadAndWrite() throws Exception { + TimestampNumberConverter converter = new TimestampNumberConverter(); + LocalDateTime localDateTime = LocalDateTime.of(2020, 1, 1, 1, 1, 1); + double excelDate = DateUtil.getExcelDate(localDateTime); + + Timestamp javaData = converter.convertToJavaData( + new ReadCellData<>(BigDecimal.valueOf(excelDate)), null, GLOBAL_CONFIGURATION); + assertEquals(Timestamp.valueOf(localDateTime), javaData); + + WriteCellData writeCellData = + converter.convertToExcelData(Timestamp.valueOf(localDateTime), null, GLOBAL_CONFIGURATION); + assertEquals(0, writeCellData.getNumberValue().compareTo(BigDecimal.valueOf(excelDate))); + } + + @Test + void numberConverterRespectsUse1904Windowing() throws Exception { + TimestampNumberConverter converter = new TimestampNumberConverter(); + LocalDateTime localDateTime = LocalDateTime.of(2020, 1, 1, 1, 1, 1); + + ExcelContentProperty contentProperty = new ExcelContentProperty(); + DateTimeFormatProperty dateTimeFormatProperty = new DateTimeFormatProperty(null, true); + contentProperty.setDateTimeFormatProperty(dateTimeFormatProperty); + + double excelDate1904 = DateUtil.getExcelDate(localDateTime, true); + Timestamp javaData = converter.convertToJavaData( + new ReadCellData<>(BigDecimal.valueOf(excelDate1904)), contentProperty, GLOBAL_CONFIGURATION); + assertEquals(Timestamp.valueOf(localDateTime), javaData); + + WriteCellData writeCellData = + converter.convertToExcelData(Timestamp.valueOf(localDateTime), contentProperty, GLOBAL_CONFIGURATION); + assertEquals(0, writeCellData.getNumberValue().compareTo(BigDecimal.valueOf(excelDate1904))); + } + + @Test + void stringConverterReadAndWrite() throws Exception { + TimestampStringConverter converter = new TimestampStringConverter(); + Timestamp timestamp = Timestamp.valueOf("2020-01-01 01:01:01"); + + Timestamp javaData = + converter.convertToJavaData(new ReadCellData<>("2020-01-01 01:01:01"), null, GLOBAL_CONFIGURATION); + assertEquals(timestamp, javaData); + + WriteCellData writeCellData = converter.convertToExcelData(timestamp, null, GLOBAL_CONFIGURATION); + assertEquals("2020-01-01 01:01:01", writeCellData.getStringValue()); + } + + @Test + void stringConverterRespectsDateTimeFormatAndLocale() throws Exception { + TimestampStringConverter converter = new TimestampStringConverter(); + Timestamp timestamp = Timestamp.valueOf("2020-01-01 01:01:01"); + + ExcelContentProperty contentProperty = new ExcelContentProperty(); + DateTimeFormatProperty dateTimeFormatProperty = new DateTimeFormatProperty("yyyy/MM/dd HH:mm:ss", null); + contentProperty.setDateTimeFormatProperty(dateTimeFormatProperty); + + GlobalConfiguration globalConfiguration = new GlobalConfiguration(); + globalConfiguration.setLocale(Locale.US); + + Timestamp javaData = converter.convertToJavaData( + new ReadCellData<>("2020/01/01 01:01:01"), contentProperty, globalConfiguration); + assertEquals(timestamp, javaData); + + WriteCellData writeCellData = converter.convertToExcelData(timestamp, contentProperty, globalConfiguration); + assertEquals("2020/01/01 01:01:01", writeCellData.getStringValue()); + } + + @Test + void dateConverterWrite() throws Exception { + TimestampDateConverter converter = new TimestampDateConverter(); + Timestamp timestamp = Timestamp.valueOf("2020-01-01 01:01:01"); + + WriteCellData writeCellData = converter.convertToExcelData(timestamp, null, GLOBAL_CONFIGURATION); + assertEquals(CellDataTypeEnum.DATE, writeCellData.getType()); + assertEquals(timestamp.toLocalDateTime(), writeCellData.getDateValue()); + assertEquals( + DateUtils.defaultDateFormat, + writeCellData.getWriteCellStyle().getDataFormatData().getFormat()); + } + + @Test + void defaultConverterLoaderRegistersTimestampConverters() { + Assertions.assertTrue(DefaultConverterLoader.loadAllConverter() + .containsKey(ConverterKeyBuild.buildKey(Timestamp.class, CellDataTypeEnum.NUMBER))); + Assertions.assertTrue(DefaultConverterLoader.loadAllConverter() + .containsKey(ConverterKeyBuild.buildKey(Timestamp.class, CellDataTypeEnum.STRING))); + + Assertions.assertTrue(DefaultConverterLoader.loadDefaultWriteConverter() + .containsKey(ConverterKeyBuild.buildKey(Timestamp.class))); + Assertions.assertTrue(DefaultConverterLoader.loadDefaultWriteConverter() + .containsKey(ConverterKeyBuild.buildKey(Timestamp.class, CellDataTypeEnum.STRING))); + } +} From bf0bb5c2fcc9b3d460bd654f3529180115828a7f Mon Sep 17 00:00:00 2001 From: Syed Ishmum Ahnaf Date: Tue, 18 Aug 2026 11:21:07 +0600 Subject: [PATCH 2/5] fix: preserve local timestamp conversion semantics --- .../sheet/converters/timestamp/TimestampDateConverter.java | 2 +- .../converters/timestamp/TimestampNumberConverter.java | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java index f855033cc..adf49c5fa 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java @@ -48,7 +48,7 @@ public Class supportJavaTypeKey() { public WriteCellData convertToExcelData( Timestamp value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception { - WriteCellData cellData = new WriteCellData<>(value); + WriteCellData cellData = new WriteCellData<>(value.toLocalDateTime()); String format = null; if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { format = contentProperty.getDateTimeFormatProperty().getFormat(); diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java index bd7bf8686..c2ca5eb5c 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java @@ -70,11 +70,12 @@ public Timestamp convertToJavaData( public WriteCellData convertToExcelData( Timestamp value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { - return new WriteCellData<>( - BigDecimal.valueOf(DateUtil.getExcelDate(value, globalConfiguration.getUse1904windowing()))); + return new WriteCellData<>(BigDecimal.valueOf( + DateUtil.getExcelDate(value.toLocalDateTime(), globalConfiguration.getUse1904windowing()))); } else { return new WriteCellData<>(BigDecimal.valueOf(DateUtil.getExcelDate( - value, contentProperty.getDateTimeFormatProperty().getUse1904windowing()))); + value.toLocalDateTime(), + contentProperty.getDateTimeFormatProperty().getUse1904windowing()))); } } } From 3a1db42e52d042fb64b6801dd5bd87069af7fc69 Mon Sep 17 00:00:00 2001 From: Syed Ishmum Ahnaf Date: Fri, 21 Aug 2026 14:47:18 +0600 Subject: [PATCH 3/5] test: verify timestamp locale formatting --- .../fesod/sheet/converter/TimestampConverterTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java index f83c50859..963073ea1 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java @@ -122,18 +122,18 @@ void stringConverterRespectsDateTimeFormatAndLocale() throws Exception { Timestamp timestamp = Timestamp.valueOf("2020-01-01 01:01:01"); ExcelContentProperty contentProperty = new ExcelContentProperty(); - DateTimeFormatProperty dateTimeFormatProperty = new DateTimeFormatProperty("yyyy/MM/dd HH:mm:ss", null); + DateTimeFormatProperty dateTimeFormatProperty = new DateTimeFormatProperty("dd MMMM yyyy HH:mm:ss", null); contentProperty.setDateTimeFormatProperty(dateTimeFormatProperty); GlobalConfiguration globalConfiguration = new GlobalConfiguration(); globalConfiguration.setLocale(Locale.US); Timestamp javaData = converter.convertToJavaData( - new ReadCellData<>("2020/01/01 01:01:01"), contentProperty, globalConfiguration); + new ReadCellData<>("01 January 2020 01:01:01"), contentProperty, globalConfiguration); assertEquals(timestamp, javaData); WriteCellData writeCellData = converter.convertToExcelData(timestamp, contentProperty, globalConfiguration); - assertEquals("2020/01/01 01:01:01", writeCellData.getStringValue()); + assertEquals("01 January 2020 01:01:01", writeCellData.getStringValue()); } @Test From 790884e71fbeade22d44996ce9ed4e45a7936893 Mon Sep 17 00:00:00 2001 From: Mikkey-f <178465103+Mikkey-f@users.noreply.github.com> Date: Wed, 14 Oct 2026 19:01:23 +0800 Subject: [PATCH 4/5] fix: adapt the Timestamp converters to current main (#1017) Carry-forward of #1019 (original implementation by Syed Ishmum Ahnaf): - Resolve the 1904 windowing flag through the shared DateUtils.isDate1904 helper, like the rest of the date converter family. Reading DateTimeFormatProperty#getUse1904windowing() directly broke on the null DEFAULT preserved by #1042 and threw a NullPointerException whenever @DateTimeFormat was present without an explicit use1904windowing value. - Drop the EasyExcel derived license header from the four new files; they are written from scratch, so the plain ASF header applies. - Align with current conventions: Class supportJavaTypeKey, single-line javadoc, and tests moved to the mirrored converters.timestamp package with qualified assertions, the thread-local cache teardown used by the sibling suites, a loader family test and an XLSX/XLS/CSV round trip. --- .../timestamp/TimestampDateConverter.java | 14 +--- .../timestamp/TimestampNumberConverter.java | 34 ++------ .../timestamp/TimestampStringConverter.java | 14 +--- .../DefaultConverterLoaderTest.java | 22 +++++ .../timestamp}/TimestampConverterTest.java | 81 ++++++++++++------- .../timestamp/TimestampRoundTripData.java | 32 ++++++++ .../timestamp/TimestampRoundTripTest.java | 48 +++++++++++ 7 files changed, 165 insertions(+), 80 deletions(-) rename fesod-sheet/src/test/java/org/apache/fesod/sheet/{converter => converters/timestamp}/TimestampConverterTest.java (64%) create mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampRoundTripData.java create mode 100644 fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampRoundTripTest.java diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java index adf49c5fa..8b9978ecd 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java @@ -17,12 +17,6 @@ * under the License. */ -/* - * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. - * - * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. - */ - package org.apache.fesod.sheet.converters.timestamp; import java.sql.Timestamp; @@ -33,14 +27,10 @@ import org.apache.fesod.sheet.util.DateUtils; import org.apache.fesod.sheet.util.WorkBookUtil; -/** - * Timestamp and date converter - * - * - */ +/** Timestamp and date converter. */ public class TimestampDateConverter implements Converter { @Override - public Class supportJavaTypeKey() { + public Class supportJavaTypeKey() { return Timestamp.class; } diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java index c2ca5eb5c..feffe9166 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java @@ -17,12 +17,6 @@ * under the License. */ -/* - * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. - * - * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. - */ - package org.apache.fesod.sheet.converters.timestamp; import java.math.BigDecimal; @@ -36,15 +30,11 @@ import org.apache.fesod.sheet.util.DateUtils; import org.apache.poi.ss.usermodel.DateUtil; -/** - * Timestamp and number converter - * - * - */ +/** Timestamp and number converter. */ public class TimestampNumberConverter implements Converter { @Override - public Class supportJavaTypeKey() { + public Class supportJavaTypeKey() { return Timestamp.class; } @@ -56,26 +46,14 @@ public CellDataTypeEnum supportExcelTypeKey() { @Override public Timestamp convertToJavaData( ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { - return Timestamp.valueOf(DateUtils.getLocalDateTime( - cellData.getNumberValue().doubleValue(), globalConfiguration.getUse1904windowing())); - } else { - return Timestamp.valueOf(DateUtils.getLocalDateTime( - cellData.getNumberValue().doubleValue(), - contentProperty.getDateTimeFormatProperty().getUse1904windowing())); - } + return Timestamp.valueOf(DateUtils.getLocalDateTime( + cellData.getNumberValue().doubleValue(), DateUtils.isDate1904(contentProperty, globalConfiguration))); } @Override public WriteCellData convertToExcelData( Timestamp value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { - return new WriteCellData<>(BigDecimal.valueOf( - DateUtil.getExcelDate(value.toLocalDateTime(), globalConfiguration.getUse1904windowing()))); - } else { - return new WriteCellData<>(BigDecimal.valueOf(DateUtil.getExcelDate( - value.toLocalDateTime(), - contentProperty.getDateTimeFormatProperty().getUse1904windowing()))); - } + return new WriteCellData<>(BigDecimal.valueOf(DateUtil.getExcelDate( + value.toLocalDateTime(), DateUtils.isDate1904(contentProperty, globalConfiguration)))); } } diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampStringConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampStringConverter.java index d5dc7efa9..f73841950 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampStringConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampStringConverter.java @@ -17,12 +17,6 @@ * under the License. */ -/* - * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. - * - * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. - */ - package org.apache.fesod.sheet.converters.timestamp; import java.sql.Timestamp; @@ -35,14 +29,10 @@ import org.apache.fesod.sheet.metadata.property.ExcelContentProperty; import org.apache.fesod.sheet.util.DateUtils; -/** - * Timestamp and string converter - * - * - */ +/** Timestamp and string converter. */ public class TimestampStringConverter implements Converter { @Override - public Class supportJavaTypeKey() { + public Class supportJavaTypeKey() { return Timestamp.class; } diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/DefaultConverterLoaderTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/DefaultConverterLoaderTest.java index 77a57ebc7..bd5f19d41 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/DefaultConverterLoaderTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/DefaultConverterLoaderTest.java @@ -20,6 +20,7 @@ package org.apache.fesod.sheet.converters; import java.sql.Time; +import java.sql.Timestamp; import java.time.Instant; import java.time.LocalTime; import java.util.Map; @@ -32,6 +33,9 @@ import org.apache.fesod.sheet.converters.sqltime.SqlTimeDateConverter; import org.apache.fesod.sheet.converters.sqltime.SqlTimeNumberConverter; import org.apache.fesod.sheet.converters.sqltime.SqlTimeStringConverter; +import org.apache.fesod.sheet.converters.timestamp.TimestampDateConverter; +import org.apache.fesod.sheet.converters.timestamp.TimestampNumberConverter; +import org.apache.fesod.sheet.converters.timestamp.TimestampStringConverter; import org.apache.fesod.sheet.converters.uuid.UUIDStringConverter; import org.apache.fesod.sheet.enums.CellDataTypeEnum; import org.junit.jupiter.api.Assertions; @@ -121,6 +125,24 @@ void loadConvertersRegistersSqlTimeFamily() { writeConverter.get(ConverterKeyBuild.buildKey(Time.class, CellDataTypeEnum.STRING))); } + @Test + void loadConvertersRegistersTimestampFamily() { + Map> allConverter = DefaultConverterLoader.loadAllConverter(); + Assertions.assertInstanceOf( + TimestampNumberConverter.class, + allConverter.get(ConverterKeyBuild.buildKey(Timestamp.class, CellDataTypeEnum.NUMBER))); + Assertions.assertInstanceOf( + TimestampStringConverter.class, + allConverter.get(ConverterKeyBuild.buildKey(Timestamp.class, CellDataTypeEnum.STRING))); + + Map> writeConverter = DefaultConverterLoader.loadDefaultWriteConverter(); + Assertions.assertInstanceOf( + TimestampDateConverter.class, writeConverter.get(ConverterKeyBuild.buildKey(Timestamp.class))); + Assertions.assertInstanceOf( + TimestampStringConverter.class, + writeConverter.get(ConverterKeyBuild.buildKey(Timestamp.class, CellDataTypeEnum.STRING))); + } + private static void assertLoadIsImmutableAndCopyIsMutable( Map> loaded, Map> copy) { Map.Entry> entry = diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampConverterTest.java similarity index 64% rename from fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java rename to fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampConverterTest.java index 963073ea1..e92a54ddb 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converter/TimestampConverterTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampConverterTest.java @@ -17,24 +17,14 @@ * under the License. */ -/* - * This file is part of the Apache Fesod (Incubating) project, which was derived from Alibaba EasyExcel. - * - * Copyright (C) 2018-2024 Alibaba Group Holding Ltd. - */ +package org.apache.fesod.sheet.converters.timestamp; -package org.apache.fesod.sheet.converter; - -import static org.junit.jupiter.api.Assertions.assertEquals; import java.math.BigDecimal; import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.Locale; import org.apache.fesod.sheet.converters.ConverterKeyBuild; import org.apache.fesod.sheet.converters.DefaultConverterLoader; -import org.apache.fesod.sheet.converters.timestamp.TimestampDateConverter; -import org.apache.fesod.sheet.converters.timestamp.TimestampNumberConverter; -import org.apache.fesod.sheet.converters.timestamp.TimestampStringConverter; import org.apache.fesod.sheet.enums.CellDataTypeEnum; import org.apache.fesod.sheet.metadata.GlobalConfiguration; import org.apache.fesod.sheet.metadata.data.ReadCellData; @@ -44,6 +34,7 @@ import org.apache.fesod.sheet.testkit.Tags; import org.apache.fesod.sheet.util.DateUtils; import org.apache.poi.ss.usermodel.DateUtil; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -52,21 +43,26 @@ * Unit tests for java.sql.Timestamp converter implementations. */ @Tag(Tags.UNIT) -public class TimestampConverterTest { +class TimestampConverterTest { private static final GlobalConfiguration GLOBAL_CONFIGURATION = new GlobalConfiguration(); + @AfterEach + void tearDown() { + DateUtils.removeThreadLocalCache(); + } + @Test void supportKeys() { - assertEquals(Timestamp.class, new TimestampDateConverter().supportJavaTypeKey()); + Assertions.assertEquals(Timestamp.class, new TimestampDateConverter().supportJavaTypeKey()); TimestampNumberConverter numberConverter = new TimestampNumberConverter(); - assertEquals(Timestamp.class, numberConverter.supportJavaTypeKey()); - assertEquals(CellDataTypeEnum.NUMBER, numberConverter.supportExcelTypeKey()); + Assertions.assertEquals(Timestamp.class, numberConverter.supportJavaTypeKey()); + Assertions.assertEquals(CellDataTypeEnum.NUMBER, numberConverter.supportExcelTypeKey()); TimestampStringConverter stringConverter = new TimestampStringConverter(); - assertEquals(Timestamp.class, stringConverter.supportJavaTypeKey()); - assertEquals(CellDataTypeEnum.STRING, stringConverter.supportExcelTypeKey()); + Assertions.assertEquals(Timestamp.class, stringConverter.supportJavaTypeKey()); + Assertions.assertEquals(CellDataTypeEnum.STRING, stringConverter.supportExcelTypeKey()); } @Test @@ -77,11 +73,11 @@ void numberConverterReadAndWrite() throws Exception { Timestamp javaData = converter.convertToJavaData( new ReadCellData<>(BigDecimal.valueOf(excelDate)), null, GLOBAL_CONFIGURATION); - assertEquals(Timestamp.valueOf(localDateTime), javaData); + Assertions.assertEquals(Timestamp.valueOf(localDateTime), javaData); WriteCellData writeCellData = converter.convertToExcelData(Timestamp.valueOf(localDateTime), null, GLOBAL_CONFIGURATION); - assertEquals(0, writeCellData.getNumberValue().compareTo(BigDecimal.valueOf(excelDate))); + Assertions.assertEquals(0, writeCellData.getNumberValue().compareTo(BigDecimal.valueOf(excelDate))); } @Test @@ -96,11 +92,40 @@ void numberConverterRespectsUse1904Windowing() throws Exception { double excelDate1904 = DateUtil.getExcelDate(localDateTime, true); Timestamp javaData = converter.convertToJavaData( new ReadCellData<>(BigDecimal.valueOf(excelDate1904)), contentProperty, GLOBAL_CONFIGURATION); - assertEquals(Timestamp.valueOf(localDateTime), javaData); + Assertions.assertEquals(Timestamp.valueOf(localDateTime), javaData); + + WriteCellData writeCellData = + converter.convertToExcelData(Timestamp.valueOf(localDateTime), contentProperty, GLOBAL_CONFIGURATION); + Assertions.assertEquals(0, writeCellData.getNumberValue().compareTo(BigDecimal.valueOf(excelDate1904))); + } + + @Test + void numberConverterFallsBackToGlobalWindowingWhenFormatLeaves1904Unset() throws Exception { + TimestampNumberConverter converter = new TimestampNumberConverter(); + LocalDateTime localDateTime = LocalDateTime.of(2020, 1, 1, 1, 1, 1); + + ExcelContentProperty contentProperty = new ExcelContentProperty(); + contentProperty.setDateTimeFormatProperty(new DateTimeFormatProperty("yyyy/MM/dd HH:mm:ss", null)); + + double excelDate = DateUtil.getExcelDate(localDateTime); + Timestamp javaData = converter.convertToJavaData( + new ReadCellData<>(BigDecimal.valueOf(excelDate)), contentProperty, GLOBAL_CONFIGURATION); + Assertions.assertEquals(Timestamp.valueOf(localDateTime), javaData); WriteCellData writeCellData = converter.convertToExcelData(Timestamp.valueOf(localDateTime), contentProperty, GLOBAL_CONFIGURATION); - assertEquals(0, writeCellData.getNumberValue().compareTo(BigDecimal.valueOf(excelDate1904))); + Assertions.assertEquals(0, writeCellData.getNumberValue().compareTo(BigDecimal.valueOf(excelDate))); + + GlobalConfiguration globalConfiguration1904 = new GlobalConfiguration(); + globalConfiguration1904.setUse1904windowing(Boolean.TRUE); + double excelDate1904 = DateUtil.getExcelDate(localDateTime, true); + Timestamp javaData1904 = converter.convertToJavaData( + new ReadCellData<>(BigDecimal.valueOf(excelDate1904)), contentProperty, globalConfiguration1904); + Assertions.assertEquals(Timestamp.valueOf(localDateTime), javaData1904); + + WriteCellData writeCellData1904 = converter.convertToExcelData( + Timestamp.valueOf(localDateTime), contentProperty, globalConfiguration1904); + Assertions.assertEquals(0, writeCellData1904.getNumberValue().compareTo(BigDecimal.valueOf(excelDate1904))); } @Test @@ -110,10 +135,10 @@ void stringConverterReadAndWrite() throws Exception { Timestamp javaData = converter.convertToJavaData(new ReadCellData<>("2020-01-01 01:01:01"), null, GLOBAL_CONFIGURATION); - assertEquals(timestamp, javaData); + Assertions.assertEquals(timestamp, javaData); WriteCellData writeCellData = converter.convertToExcelData(timestamp, null, GLOBAL_CONFIGURATION); - assertEquals("2020-01-01 01:01:01", writeCellData.getStringValue()); + Assertions.assertEquals("2020-01-01 01:01:01", writeCellData.getStringValue()); } @Test @@ -130,10 +155,10 @@ void stringConverterRespectsDateTimeFormatAndLocale() throws Exception { Timestamp javaData = converter.convertToJavaData( new ReadCellData<>("01 January 2020 01:01:01"), contentProperty, globalConfiguration); - assertEquals(timestamp, javaData); + Assertions.assertEquals(timestamp, javaData); WriteCellData writeCellData = converter.convertToExcelData(timestamp, contentProperty, globalConfiguration); - assertEquals("01 January 2020 01:01:01", writeCellData.getStringValue()); + Assertions.assertEquals("01 January 2020 01:01:01", writeCellData.getStringValue()); } @Test @@ -142,9 +167,9 @@ void dateConverterWrite() throws Exception { Timestamp timestamp = Timestamp.valueOf("2020-01-01 01:01:01"); WriteCellData writeCellData = converter.convertToExcelData(timestamp, null, GLOBAL_CONFIGURATION); - assertEquals(CellDataTypeEnum.DATE, writeCellData.getType()); - assertEquals(timestamp.toLocalDateTime(), writeCellData.getDateValue()); - assertEquals( + Assertions.assertEquals(CellDataTypeEnum.DATE, writeCellData.getType()); + Assertions.assertEquals(timestamp.toLocalDateTime(), writeCellData.getDateValue()); + Assertions.assertEquals( DateUtils.defaultDateFormat, writeCellData.getWriteCellStyle().getDataFormatData().getFormat()); } diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampRoundTripData.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampRoundTripData.java new file mode 100644 index 000000000..468665715 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampRoundTripData.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fesod.sheet.converters.timestamp; + +import java.sql.Timestamp; +import lombok.Getter; +import lombok.Setter; +import org.apache.fesod.sheet.annotation.ExcelProperty; + +@Getter +@Setter +public class TimestampRoundTripData { + @ExcelProperty("timestamp") + private Timestamp timestamp; +} diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampRoundTripTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampRoundTripTest.java new file mode 100644 index 000000000..73a5541d9 --- /dev/null +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampRoundTripTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fesod.sheet.converters.timestamp; + +import java.io.File; +import java.sql.Timestamp; +import java.util.Collections; +import java.util.List; +import org.apache.fesod.sheet.testkit.Tags; +import org.apache.fesod.sheet.testkit.base.AbstractExcelTest; +import org.apache.fesod.sheet.testkit.enums.ExcelFormat; +import org.apache.fesod.sheet.testkit.helpers.RoundTripHelper; +import org.apache.fesod.sheet.testkit.params.ExcelFormatSource; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.params.ParameterizedTest; + +@Tag(Tags.ROUND_TRIP) +class TimestampRoundTripTest extends AbstractExcelTest { + @ParameterizedTest + @ExcelFormatSource + void roundTrip(ExcelFormat format) throws Exception { + File file = createTempFile(format); + TimestampRoundTripData input = new TimestampRoundTripData(); + input.setTimestamp(Timestamp.valueOf("2020-01-01 01:01:01")); + List result = + RoundTripHelper.writeAndRead(file, TimestampRoundTripData.class, Collections.singletonList(input)); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals(input.getTimestamp(), result.get(0).getTimestamp()); + } +} From 59bf75515224880a25bb5ede5a1573ae67561250 Mon Sep 17 00:00:00 2001 From: Mikkey-f <178465103+Mikkey-f@users.noreply.github.com> Date: Thu, 15 Oct 2026 20:06:49 +0800 Subject: [PATCH 5/5] fix: normalize empty date format and guard invalid dates in Timestamp converters (#1017) --- .../timestamp/TimestampDateConverter.java | 3 ++- .../timestamp/TimestampNumberConverter.java | 6 ++++-- .../timestamp/TimestampConverterTest.java | 21 +++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java index 8b9978ecd..7f8386c55 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampDateConverter.java @@ -43,7 +43,8 @@ public WriteCellData convertToExcelData( if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { format = contentProperty.getDateTimeFormatProperty().getFormat(); } - WorkBookUtil.fillDataFormat(cellData, format, DateUtils.defaultDateFormat); + WorkBookUtil.fillDataFormat( + cellData, format == null || format.isEmpty() ? null : format, DateUtils.defaultDateFormat); return cellData; } } diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java index feffe9166..35e53f292 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/converters/timestamp/TimestampNumberConverter.java @@ -21,6 +21,7 @@ import java.math.BigDecimal; import java.sql.Timestamp; +import java.time.LocalDateTime; import org.apache.fesod.sheet.converters.Converter; import org.apache.fesod.sheet.enums.CellDataTypeEnum; import org.apache.fesod.sheet.metadata.GlobalConfiguration; @@ -46,8 +47,9 @@ public CellDataTypeEnum supportExcelTypeKey() { @Override public Timestamp convertToJavaData( ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { - return Timestamp.valueOf(DateUtils.getLocalDateTime( - cellData.getNumberValue().doubleValue(), DateUtils.isDate1904(contentProperty, globalConfiguration))); + LocalDateTime localDateTime = DateUtils.getLocalDateTime( + cellData.getNumberValue().doubleValue(), DateUtils.isDate1904(contentProperty, globalConfiguration)); + return localDateTime == null ? null : Timestamp.valueOf(localDateTime); } @Override diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampConverterTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampConverterTest.java index e92a54ddb..c58c15d08 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampConverterTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/converters/timestamp/TimestampConverterTest.java @@ -128,6 +128,13 @@ void numberConverterFallsBackToGlobalWindowingWhenFormatLeaves1904Unset() throws Assertions.assertEquals(0, writeCellData1904.getNumberValue().compareTo(BigDecimal.valueOf(excelDate1904))); } + @Test + void numberConverterReturnsNullForInvalidExcelDate() throws Exception { + TimestampNumberConverter converter = new TimestampNumberConverter(); + Assertions.assertNull( + converter.convertToJavaData(new ReadCellData<>(BigDecimal.valueOf(-1)), null, GLOBAL_CONFIGURATION)); + } + @Test void stringConverterReadAndWrite() throws Exception { TimestampStringConverter converter = new TimestampStringConverter(); @@ -174,6 +181,20 @@ void dateConverterWrite() throws Exception { writeCellData.getWriteCellStyle().getDataFormatData().getFormat()); } + @Test + void dateConverterFallsBackToDefaultFormatForEmptyDateTimeFormat() throws Exception { + TimestampDateConverter converter = new TimestampDateConverter(); + Timestamp timestamp = Timestamp.valueOf("2020-01-01 01:01:01"); + + ExcelContentProperty contentProperty = new ExcelContentProperty(); + contentProperty.setDateTimeFormatProperty(new DateTimeFormatProperty("", false)); + + WriteCellData writeCellData = converter.convertToExcelData(timestamp, contentProperty, GLOBAL_CONFIGURATION); + Assertions.assertEquals( + DateUtils.defaultDateFormat, + writeCellData.getWriteCellStyle().getDataFormatData().getFormat()); + } + @Test void defaultConverterLoaderRegistersTimestampConverters() { Assertions.assertTrue(DefaultConverterLoader.loadAllConverter()