Skip to content

Commit cc72e8d

Browse files
committed
Fix IsoFormattingDateDataFormatter
The format used the ISO_OFFSET_DATE_TIME is the wrong one and leads to errors formatting the date. Instead we should have used the ISO_LOCAL_DATE_TIME instead. This commit fixes that and also includes a bug fix for the XLSX variant as that didn't use the overridden method. The correct method for this has been overridden now also formatting the dates/times for that using the ISO_LOCAL_DATE_TIME format. Tests for different types have been added as well, as to make sure this keeps working for newer versions. Closes: #121
1 parent b6e2b97 commit cc72e8d

File tree

5 files changed

+148
-2
lines changed

5 files changed

+148
-2
lines changed

spring-batch-excel/src/main/java/org/springframework/batch/extensions/excel/IsoFormattingDateDataFormatter.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import org.apache.poi.ss.usermodel.FormulaEvaluator;
2929

3030
/**
31-
* Specialized subclass for additionally formatting the date into an ISO date/time.
31+
* Specialized subclass for formatting the date into an ISO date/time and ignore the format as given in the Excel file.
3232
*
3333
* @author Marten Deinum
3434
*
@@ -44,6 +44,15 @@ public IsoFormattingDateDataFormatter(Locale locale) {
4444
super(locale);
4545
}
4646

47+
@Override
48+
public String formatRawCellContents(double value, int formatIndex, String formatString, boolean use1904Windowing) {
49+
if (DateUtil.isADateFormat(formatIndex, formatString) && DateUtil.isValidExcelDate(value)) {
50+
return super.formatRawCellContents(value, formatIndex, "yyyy-MM-ddTHH:mm:ss",
51+
use1904Windowing);
52+
}
53+
return super.formatRawCellContents(value, formatIndex, formatString, use1904Windowing);
54+
}
55+
4756
@Override
4857
public String formatCellValue(Cell cell, FormulaEvaluator evaluator, ConditionalFormattingEvaluator cfEvaluator) {
4958
if (cell == null) {
@@ -60,7 +69,7 @@ public String formatCellValue(Cell cell, FormulaEvaluator evaluator, Conditional
6069

6170
if (cellType == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell, cfEvaluator)) {
6271
LocalDateTime value = cell.getLocalDateTimeCellValue();
63-
return (value != null) ? value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME) : "";
72+
return (value != null) ? value.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : "";
6473
}
6574
return super.formatCellValue(cell, evaluator, cfEvaluator);
6675
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.batch.extensions.excel.poi;
18+
19+
import java.util.Locale;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.springframework.batch.extensions.excel.mapping.PassThroughRowMapper;
24+
import org.springframework.batch.item.ExecutionContext;
25+
import org.springframework.core.io.ClassPathResource;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
30+
public class PoiItemReaderTypesTest {
31+
32+
@Test
33+
public void shouldBeAbleToReadMultipleTypes() throws Exception {
34+
var reader = new PoiItemReader<String[]>();
35+
reader.setResource(new ClassPathResource("types.xls"));
36+
reader.setRowMapper(new PassThroughRowMapper());
37+
reader.setLinesToSkip(1); // Skip header
38+
reader.setUserLocale(Locale.US); // Use a Locale to not be dependent on environment
39+
reader.afterPropertiesSet();
40+
41+
42+
reader.open(new ExecutionContext());
43+
44+
var row1 = reader.read();
45+
var row2 = reader.read();
46+
assertThat(row1).containsExactly("1", "1.0", "5/12/24", "13:14:55", "5/12/24 13:14", "hello world");
47+
assertThat(row2).containsExactly("2", "2.5", "8/8/23", "11:12:13", "8/8/23 11:12", "world hello");
48+
49+
}
50+
51+
@Test
52+
public void shouldBeAbleToReadMultipleTypesWithDatesAsIso() throws Exception {
53+
var reader = new PoiItemReader<String[]>();
54+
reader.setResource(new ClassPathResource("types.xls"));
55+
reader.setRowMapper(new PassThroughRowMapper());
56+
reader.setLinesToSkip(1); // Skip header
57+
reader.setUserLocale(Locale.US); // Use a Locale to not be dependent on environment
58+
reader.setDatesAsIso(true);
59+
reader.afterPropertiesSet();
60+
61+
reader.open(new ExecutionContext());
62+
63+
var row1 = reader.read();
64+
var row2 = reader.read();
65+
assertThat(row1).containsExactly("1", "1.0", "2024-05-12T00:00:00", "1899-12-31T13:14:55", "2024-05-12T13:14:55", "hello world");
66+
assertThat(row2).containsExactly("2", "2.5", "2023-08-08T00:00:00", "1899-12-31T11:12:13", "2023-08-08T11:12:13", "world hello");
67+
68+
}
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.batch.extensions.excel.streaming;
18+
19+
import java.util.Locale;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.springframework.batch.extensions.excel.mapping.PassThroughRowMapper;
24+
import org.springframework.batch.item.ExecutionContext;
25+
import org.springframework.core.io.ClassPathResource;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
public class StreamingXlsxTypesTest {
30+
31+
@Test
32+
public void shouldBeAbleToReadMultipleTypes() throws Exception {
33+
var reader = new StreamingXlsxItemReader<String[]>();
34+
reader.setResource(new ClassPathResource("types.xlsx"));
35+
reader.setRowMapper(new PassThroughRowMapper());
36+
reader.setLinesToSkip(1); // Skip header
37+
reader.setUserLocale(Locale.US); // Use a Locale to not be dependent on environment
38+
reader.afterPropertiesSet();
39+
40+
reader.open(new ExecutionContext());
41+
42+
var row1 = reader.read();
43+
var row2 = reader.read();
44+
assertThat(row1).containsExactly("1", "1.0", "5/12/24", "13:14:55", "5/12/24 13:14", "hello world");
45+
assertThat(row2).containsExactly("2", "2.5", "8/8/23", "11:12:13", "8/8/23 11:12", "world hello");
46+
47+
}
48+
49+
@Test
50+
public void shouldBeAbleToReadMultipleTypesWithDatesAsIso() throws Exception {
51+
var reader = new StreamingXlsxItemReader<String[]>();
52+
reader.setResource(new ClassPathResource("types.xlsx"));
53+
reader.setRowMapper(new PassThroughRowMapper());
54+
reader.setLinesToSkip(1); // Skip header
55+
reader.setUserLocale(Locale.US); // Use a Locale to not be dependent on environment
56+
reader.setDatesAsIso(true);
57+
reader.afterPropertiesSet();
58+
59+
reader.open(new ExecutionContext());
60+
61+
var row1 = reader.read();
62+
var row2 = reader.read();
63+
assertThat(row1).containsExactly("1", "1.0", "2024-05-12T00:00:00", "1899-12-31T13:14:55", "2024-05-12T13:14:55", "hello world");
64+
assertThat(row2).containsExactly("2", "2.5", "2023-08-08T00:00:00", "1899-12-31T11:12:13", "2023-08-08T11:12:13", "world hello");
65+
66+
}
67+
68+
}
Binary file not shown.
9.02 KB
Binary file not shown.

0 commit comments

Comments
 (0)