Skip to content

Commit 50f93d3

Browse files
authored
Merge pull request #140 from spring-projects/improve-isodateformatter
Improve IsoFormattingDateDataFormatter
2 parents 8081301 + 9b03569 commit 50f93d3

File tree

4 files changed

+98
-21
lines changed

4 files changed

+98
-21
lines changed

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

+28-11
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@
2525
import org.apache.poi.ss.usermodel.CellType;
2626
import org.apache.poi.ss.usermodel.DataFormatter;
2727
import org.apache.poi.ss.usermodel.DateUtil;
28+
import org.apache.poi.ss.usermodel.ExcelNumberFormat;
2829
import org.apache.poi.ss.usermodel.FormulaEvaluator;
2930

3031
/**
3132
* Specialized subclass for formatting the date into an ISO date/time and ignore the format as given in the Excel file.
3233
*
3334
* @author Marten Deinum
34-
*
35-
* @see DateTimeFormatter#ISO_OFFSET_DATE_TIME
3635
*/
3736
public class IsoFormattingDateDataFormatter extends DataFormatter {
3837

@@ -46,9 +45,10 @@ public IsoFormattingDateDataFormatter(Locale locale) {
4645

4746
@Override
4847
public String formatRawCellContents(double value, int formatIndex, String formatString, boolean use1904Windowing) {
48+
4949
if (DateUtil.isADateFormat(formatIndex, formatString) && DateUtil.isValidExcelDate(value)) {
50-
return super.formatRawCellContents(value, formatIndex, "yyyy-MM-ddTHH:mm:ss",
51-
use1904Windowing);
50+
String formatToUse = determineFormat(formatIndex);
51+
return super.formatRawCellContents(value, formatIndex, formatToUse, use1904Windowing);
5252
}
5353
return super.formatRawCellContents(value, formatIndex, formatString, use1904Windowing);
5454
}
@@ -60,17 +60,34 @@ public String formatCellValue(Cell cell, FormulaEvaluator evaluator, Conditional
6060
}
6161

6262
CellType cellType = cell.getCellType();
63-
if (cellType == CellType.FORMULA) {
64-
if (evaluator == null) {
65-
return cell.getCellFormula();
66-
}
67-
cellType = evaluator.evaluateFormulaCell(cell);
63+
if (cellType == CellType.FORMULA && useCachedValuesForFormulaCells()) {
64+
cellType = cell.getCachedFormulaResultType();
6865
}
6966

70-
if (cellType == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell, cfEvaluator)) {
67+
if (cellType != CellType.STRING && DateUtil.isCellDateFormatted(cell, cfEvaluator)) {
68+
String formatToUse = determineFormat(ExcelNumberFormat.from(cell, cfEvaluator).getIdx());
7169
LocalDateTime value = cell.getLocalDateTimeCellValue();
72-
return (value != null) ? value.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : "";
70+
return (value != null) ? value.format(DateTimeFormatter.ofPattern(formatToUse)) : "";
7371
}
7472
return super.formatCellValue(cell, evaluator, cfEvaluator);
7573
}
74+
75+
/**
76+
* Determine the format to use for either date, time of datetime. Based on the internal formats used by Excel.
77+
* 14, 15, 16, 17 are dates only
78+
* 18, 19, 20, 21 are times only
79+
* anything else is interpreted as a datetime, including custom formats that might be in use!
80+
* @param formatIndex the format index from excel.
81+
* @return the format to use, never {@code null}.
82+
*/
83+
84+
private String determineFormat(int formatIndex) {
85+
if (formatIndex >= 14 && formatIndex < 18) {
86+
return "yyyy-MM-dd";
87+
}
88+
else if (formatIndex >= 18 && formatIndex < 22) {
89+
return "HH:mm:ss";
90+
}
91+
return "yyyy-MM-dd'T'HH:mm:ss";
92+
}
7693
}
+3-5
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import static org.assertj.core.api.Assertions.assertThat;
2828

2929

30-
class PoiItemReaderTypesTests {
30+
class PoiItemReaderXlsTypesTests {
3131

3232
@Test
3333
void shouldBeAbleToReadMultipleTypes() throws Exception {
@@ -38,7 +38,6 @@ void shouldBeAbleToReadMultipleTypes() throws Exception {
3838
reader.setUserLocale(Locale.US); // Use a Locale to not be dependent on environment
3939
reader.afterPropertiesSet();
4040

41-
4241
reader.open(new ExecutionContext());
4342

4443
var row1 = reader.read();
@@ -62,8 +61,7 @@ void shouldBeAbleToReadMultipleTypesWithDatesAsIso() throws Exception {
6261

6362
var row1 = reader.read();
6463
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-
64+
assertThat(row1).containsExactly("1", "1.0", "2024-05-12", "13:14:55", "2024-05-12T13:14:55", "hello world");
65+
assertThat(row2).containsExactly("2", "2.5", "2023-08-08", "11:12:13", "2023-08-08T11:12:13", "world hello");
6866
}
6967
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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+
class PoiItemReaderXlsxTypesTests {
30+
31+
@Test
32+
void shouldBeAbleToReadMultipleTypes() throws Exception {
33+
var reader = new PoiItemReader<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+
@Test
49+
void shouldBeAbleToReadMultipleTypesWithDatesAsIso() throws Exception {
50+
var reader = new PoiItemReader<String[]>();
51+
reader.setResource(new ClassPathResource("types.xls"));
52+
reader.setRowMapper(new PassThroughRowMapper());
53+
reader.setLinesToSkip(1); // Skip header
54+
reader.setUserLocale(Locale.US); // Use a Locale to not be dependent on environment
55+
reader.setDatesAsIso(true);
56+
reader.afterPropertiesSet();
57+
58+
reader.open(new ExecutionContext());
59+
60+
var row1 = reader.read();
61+
var row2 = reader.read();
62+
assertThat(row1).containsExactly("1", "1.0", "2024-05-12", "13:14:55", "2024-05-12T13:14:55", "hello world");
63+
assertThat(row2).containsExactly("2", "2.5", "2023-08-08", "11:12:13", "2023-08-08T11:12:13", "world hello");
64+
}
65+
}

spring-batch-excel/src/test/java/org/springframework/batch/extensions/excel/streaming/StreamingXlsxTypesTests.java

+2-5
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ void shouldBeAbleToReadMultipleTypes() throws Exception {
4343
var row2 = reader.read();
4444
assertThat(row1).containsExactly("1", "1.0", "5/12/24", "13:14:55", "5/12/24 13:14", "hello world");
4545
assertThat(row2).containsExactly("2", "2.5", "8/8/23", "11:12:13", "8/8/23 11:12", "world hello");
46-
4746
}
4847

4948
@Test
@@ -60,9 +59,7 @@ void shouldBeAbleToReadMultipleTypesWithDatesAsIso() throws Exception {
6059

6160
var row1 = reader.read();
6261
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-
62+
assertThat(row1).containsExactly("1", "1.0", "2024-05-12", "13:14:55", "2024-05-12T13:14:55", "hello world");
63+
assertThat(row2).containsExactly("2", "2.5", "2023-08-08", "11:12:13", "2023-08-08T11:12:13", "world hello");
6664
}
67-
6865
}

0 commit comments

Comments
 (0)