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..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,10 +20,15 @@ package org.apache.fesod.sheet.format; import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +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 +125,88 @@ 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 + 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); + + 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 + 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); + + 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); + } + + /** + * 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 (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);
+
+ // 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