From f74d5ee7a7f95d98e864d792f8237b6ee43a6d06 Mon Sep 17 00:00:00 2001 From: ian zhang Date: Thu, 16 Jul 2026 16:52:39 +0800 Subject: [PATCH 1/4] fix: CsvCell crashes on java.sql.Date/Time due to unsupported toInstant() The SQL date type fix in 6374a8c was only applied to WriteCellData, not to CsvCell.setCellValueImpl(Date). The old code calls value.toInstant() unconditionally, which throws UnsupportedOperationException for java.sql.Date and java.sql.Time on Java 9+ (these types override toInstant() to throw because they represent date-only/time-only values). Fix: Add the same instanceof checks as WriteCellData - use toLocalDate() .atStartOfDay() for java.sql.Date and toLocalTime().atDate(...) for java.sql.Time, falling back to toInstant() for regular java.util.Date. Tests: Add CsvRowTest.testCsvCellSqlDateConversion and testCsvCellSqlTimeConversion to verify correct conversion. --- .../fesod/sheet/metadata/csv/CsvCell.java | 8 +++- .../apache/fesod/sheet/format/CsvRowTest.java | 44 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/fesod-sheet/src/main/java/org/apache/fesod/sheet/metadata/csv/CsvCell.java b/fesod-sheet/src/main/java/org/apache/fesod/sheet/metadata/csv/CsvCell.java index 740f1f263..f8e3b9f68 100644 --- a/fesod-sheet/src/main/java/org/apache/fesod/sheet/metadata/csv/CsvCell.java +++ b/fesod-sheet/src/main/java/org/apache/fesod/sheet/metadata/csv/CsvCell.java @@ -164,7 +164,13 @@ protected void setCellValueImpl(Date value) { if (value == null) { return; } - this.dateValue = LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault()); + if (value instanceof java.sql.Date) { + this.dateValue = ((java.sql.Date) value).toLocalDate().atStartOfDay(); + } else if (value instanceof java.sql.Time) { + this.dateValue = ((java.sql.Time) value).toLocalTime().atDate(java.time.LocalDate.of(1970, 1, 1)); + } else { + this.dateValue = LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault()); + } this.cellType = CellType.NUMERIC; this.numericCellType = NumericCellTypeEnum.DATE; } diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java index 2af4a1b7c..cd3d6917e 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java @@ -20,10 +20,13 @@ package org.apache.fesod.sheet.format; import java.io.File; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.List; import org.apache.fesod.sheet.FastExcel; +import org.apache.fesod.sheet.metadata.csv.CsvCell; import org.apache.fesod.sheet.metadata.csv.CsvRow; import org.apache.fesod.sheet.metadata.csv.CsvSheet; import org.apache.fesod.sheet.metadata.csv.CsvWorkbook; @@ -120,6 +123,47 @@ void testCsvWriteWithModelShouldSuccess() { .doWrite(modelData()); } + /** + * Verifies that {@link CsvCell} handles {@link java.sql.Date} the same way as + * {@link org.apache.fesod.sheet.metadata.data.WriteCellData}: the date is extracted + * via {@code toLocalDate().atStartOfDay()}, stripping any time component that may + * exist in the underlying milliseconds (common when JDBC drivers create + * {@code java.sql.Date} from a {@code java.util.Date} with time info). + */ + @Test + void testCsvCellSqlDateConversion() { + // Create a java.sql.Date from a java.util.Date that has a time component + java.util.Date utilDate = new java.util.Date(2023 - 1900, Calendar.JUNE, 15, 23, 30, 0); + java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); + + Cell cell = csvRow.createCell(0, CellType.NUMERIC); + cell.setCellValue(sqlDate); + + LocalDateTime dateValue = ((CsvCell) cell).getLocalDateTimeCellValue(); + // java.sql.Date is date-only: time component must be stripped to midnight + Assertions.assertEquals(LocalDateTime.of(2023, 6, 15, 0, 0, 0), dateValue); + } + + /** + * Verifies that {@link CsvCell} handles {@link java.sql.Time} the same way as + * {@link org.apache.fesod.sheet.metadata.data.WriteCellData}: the time is extracted + * via {@code toLocalTime().atDate(LocalDate.of(1970, 1, 1))}, stripping any date + * component that may exist in the underlying milliseconds. + */ + @Test + void testCsvCellSqlTimeConversion() { + // Create a java.sql.Time from a java.util.Date that has a date component + java.util.Date utilDate = new java.util.Date(2023 - 1900, Calendar.JUNE, 15, 12, 30, 45); + java.sql.Time sqlTime = new java.sql.Time(utilDate.getTime()); + + Cell cell = csvRow.createCell(0, CellType.NUMERIC); + cell.setCellValue(sqlTime); + + LocalDateTime dateValue = ((CsvCell) cell).getLocalDateTimeCellValue(); + // java.sql.Time is time-only: date component must be normalized to 1970-01-01 + Assertions.assertEquals(LocalDateTime.of(1970, 1, 1, 12, 30, 45), dateValue); + } + private static List modelData() { List data = new ArrayList<>(); data.add(new SimpleCsvData("1", "Jackson", "20")); From 6e17e1d33f60487e541f52cf5ad52ee075b44fa3 Mon Sep 17 00:00:00 2001 From: ian zhang Date: Thu, 16 Jul 2026 18:52:36 +0800 Subject: [PATCH 2/4] test: add real-file CSV integration test for SQL Date/Time --- .../apache/fesod/sheet/format/CsvRowTest.java | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java index cd3d6917e..07cacade1 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java @@ -20,6 +20,9 @@ package org.apache.fesod.sheet.format; import java.io.File; +import java.io.FileWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; @@ -164,6 +167,46 @@ void testCsvCellSqlTimeConversion() { Assertions.assertEquals(LocalDateTime.of(1970, 1, 1, 12, 30, 45), dateValue); } + /** + * Real-file integration test: writes a physical CSV file containing + * {@code java.sql.Date} and {@code java.sql.Time} values via the + * {@link CsvCell} API, then reads the file back to verify the output. + *

+ * Without the fix, {@code CsvCell.setCellValueImpl(Date)} calls + * {@code value.toInstant()} which throws {@code UnsupportedOperationException} + * on Java 9+ for {@code java.sql.Date}/{@code java.sql.Time}. + */ + @Test + void csvWrite_withSqlDateAndTime_producesCorrectFile() throws Exception { + File csvFile = new File(tempDir, "sql-date-test.csv"); + + try (FileWriter writer = new FileWriter(csvFile)) { + CsvWorkbook workbook = new CsvWorkbook( + writer, null, false, false, StandardCharsets.UTF_8, false); + CsvSheet sheet = (CsvSheet) workbook.createSheet(); + CsvRow row = (CsvRow) sheet.createRow(0); + + // java.sql.Date — without fix: UnsupportedOperationException + Cell dateCell = row.createCell(0, CellType.NUMERIC); + dateCell.setCellValue(java.sql.Date.valueOf("2024-01-15")); + + // java.sql.Time — without fix: UnsupportedOperationException + Cell timeCell = row.createCell(1, CellType.NUMERIC); + timeCell.setCellValue(java.sql.Time.valueOf("12:30:45")); + + sheet.close(); + } + + // Read file back and verify date/time strings + List lines = Files.readAllLines(csvFile.toPath(), StandardCharsets.UTF_8); + Assertions.assertEquals(1, lines.size()); + String line = lines.get(0); + Assertions.assertTrue(line.contains("2024-01-15"), + "CSV should contain date 2024-01-15, got: " + line); + Assertions.assertTrue(line.contains("12:30:45"), + "CSV should contain time 12:30:45, got: " + line); + } + private static List modelData() { List data = new ArrayList<>(); data.add(new SimpleCsvData("1", "Jackson", "20")); From aa1be17d568994ed85d5f810488d222b507e02ce Mon Sep 17 00:00:00 2001 From: ian zhang Date: Fri, 17 Jul 2026 19:47:52 +0800 Subject: [PATCH 3/4] refactor(skill): update import statements for ReadBasicParameter and WriteBasicParameter Updated the import statements in SKILL.md to replace references to cn.idev.excel with org.apache.fesod for ReadBasicParameter and WriteBasicParameter. This change aligns the code with the new Fesod package structure. --- .../java/org/apache/fesod/sheet/format/CsvRowTest.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java index 07cacade1..d0e079d3c 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java @@ -181,8 +181,7 @@ void csvWrite_withSqlDateAndTime_producesCorrectFile() throws Exception { File csvFile = new File(tempDir, "sql-date-test.csv"); try (FileWriter writer = new FileWriter(csvFile)) { - CsvWorkbook workbook = new CsvWorkbook( - writer, null, false, false, StandardCharsets.UTF_8, false); + CsvWorkbook workbook = new CsvWorkbook(writer, null, false, false, StandardCharsets.UTF_8, false); CsvSheet sheet = (CsvSheet) workbook.createSheet(); CsvRow row = (CsvRow) sheet.createRow(0); @@ -201,10 +200,8 @@ void csvWrite_withSqlDateAndTime_producesCorrectFile() throws Exception { List lines = Files.readAllLines(csvFile.toPath(), StandardCharsets.UTF_8); Assertions.assertEquals(1, lines.size()); String line = lines.get(0); - Assertions.assertTrue(line.contains("2024-01-15"), - "CSV should contain date 2024-01-15, got: " + line); - Assertions.assertTrue(line.contains("12:30:45"), - "CSV should contain time 12:30:45, got: " + line); + Assertions.assertTrue(line.contains("2024-01-15"), "CSV should contain date 2024-01-15, got: " + line); + Assertions.assertTrue(line.contains("12:30:45"), "CSV should contain time 12:30:45, got: " + line); } private static List modelData() { From 5a2713522101083ec76255ba038c17df1b4cea61 Mon Sep 17 00:00:00 2001 From: ian zhang Date: Fri, 17 Jul 2026 19:59:41 +0800 Subject: [PATCH 4/4] fix: address Copilot review comments on CsvRowTest - Replace FileWriter with Files.newBufferedWriter(path, UTF_8) to ensure platform-independent encoding consistent with CsvWorkbook's UTF-8 config - Replace deprecated java.util.Date(int,int,...) constructors with Calendar-based construction to avoid deprecation warnings --- .../org/apache/fesod/sheet/format/CsvRowTest.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java b/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java index d0e079d3c..7d65df234 100644 --- a/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java +++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/format/CsvRowTest.java @@ -20,7 +20,6 @@ package org.apache.fesod.sheet.format; import java.io.File; -import java.io.FileWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.time.LocalDateTime; @@ -136,8 +135,10 @@ void testCsvWriteWithModelShouldSuccess() { @Test void testCsvCellSqlDateConversion() { // Create a java.sql.Date from a java.util.Date that has a time component - java.util.Date utilDate = new java.util.Date(2023 - 1900, Calendar.JUNE, 15, 23, 30, 0); - java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime()); + Calendar cal = Calendar.getInstance(); + cal.set(2023, Calendar.JUNE, 15, 23, 30, 0); + cal.set(Calendar.MILLISECOND, 0); + java.sql.Date sqlDate = new java.sql.Date(cal.getTimeInMillis()); Cell cell = csvRow.createCell(0, CellType.NUMERIC); cell.setCellValue(sqlDate); @@ -156,8 +157,10 @@ void testCsvCellSqlDateConversion() { @Test void testCsvCellSqlTimeConversion() { // Create a java.sql.Time from a java.util.Date that has a date component - java.util.Date utilDate = new java.util.Date(2023 - 1900, Calendar.JUNE, 15, 12, 30, 45); - java.sql.Time sqlTime = new java.sql.Time(utilDate.getTime()); + Calendar cal = Calendar.getInstance(); + cal.set(2023, Calendar.JUNE, 15, 12, 30, 45); + cal.set(Calendar.MILLISECOND, 0); + java.sql.Time sqlTime = new java.sql.Time(cal.getTimeInMillis()); Cell cell = csvRow.createCell(0, CellType.NUMERIC); cell.setCellValue(sqlTime); @@ -180,7 +183,7 @@ void testCsvCellSqlTimeConversion() { void csvWrite_withSqlDateAndTime_producesCorrectFile() throws Exception { File csvFile = new File(tempDir, "sql-date-test.csv"); - try (FileWriter writer = new FileWriter(csvFile)) { + try (java.io.Writer writer = Files.newBufferedWriter(csvFile.toPath(), StandardCharsets.UTF_8)) { CsvWorkbook workbook = new CsvWorkbook(writer, null, false, false, StandardCharsets.UTF_8, false); CsvSheet sheet = (CsvSheet) workbook.createSheet(); CsvRow row = (CsvRow) sheet.createRow(0);