Skip to content

Commit 868849e

Browse files
thswlsqlsfmbenhassine
authored andcommitted
Add parameter converters for ZonedDateTime and OffsetDateTime
- Add ZonedDateTimeToStringConverter and StringToZonedDateTimeConverter - Add OffsetDateTimeToStringConverter and StringToOffsetDateTimeConverter - Register new converters in DefaultJobParametersConverter - Add unit tests for all new converters - Add integration tests for JobParameters with ZonedDateTime/OffsetDateTime - Update JavaDoc in DefaultJobParametersConverter Closes #5178 Signed-off-by: eunbin <[email protected]>
1 parent 3d8d122 commit 868849e

10 files changed

+489
-2
lines changed

spring-batch-core/src/main/java/org/springframework/batch/core/converter/DefaultJobParametersConverter.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2025 the original author or authors.
2+
* Copyright 2006-present the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -64,6 +64,10 @@
6464
* {@link java.time.format.DateTimeFormatter#ISO_LOCAL_TIME} format</li>
6565
* <li>{@link java.time.LocalDateTime}: in the
6666
* {@link java.time.format.DateTimeFormatter#ISO_LOCAL_DATE_TIME} format</li>
67+
* <li>{@link java.time.ZonedDateTime}: in the
68+
* {@link java.time.format.DateTimeFormatter#ISO_ZONED_DATE_TIME} format</li>
69+
* <li>{@link java.time.OffsetDateTime}: in the
70+
* {@link java.time.format.DateTimeFormatter#ISO_OFFSET_DATE_TIME} format</li>
6771
* </ul>
6872
*
6973
* @author Dave Syer
@@ -85,6 +89,10 @@ public DefaultJobParametersConverter() {
8589
conversionService.addConverter(new StringToLocalTimeConverter());
8690
conversionService.addConverter(new LocalDateTimeToStringConverter());
8791
conversionService.addConverter(new StringToLocalDateTimeConverter());
92+
conversionService.addConverter(new ZonedDateTimeToStringConverter());
93+
conversionService.addConverter(new StringToZonedDateTimeConverter());
94+
conversionService.addConverter(new OffsetDateTimeToStringConverter());
95+
conversionService.addConverter(new StringToOffsetDateTimeConverter());
8896
this.conversionService = conversionService;
8997
}
9098

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2026-present 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+
package org.springframework.batch.core.converter;
17+
18+
import java.time.OffsetDateTime;
19+
import java.time.format.DateTimeFormatter;
20+
21+
import org.springframework.core.convert.converter.Converter;
22+
23+
/**
24+
* {@link Converter} implementation from {@link OffsetDateTime} to {@link String}.
25+
* <p>
26+
* This converter formats dates according to the
27+
* {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME} format.
28+
*
29+
* @author Eunbin Son
30+
* @since 6.0.2
31+
*/
32+
public class OffsetDateTimeToStringConverter implements Converter<OffsetDateTime, String> {
33+
34+
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
35+
36+
@Override
37+
public String convert(OffsetDateTime source) {
38+
return source.format(FORMATTER);
39+
}
40+
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2026-present 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+
package org.springframework.batch.core.converter;
17+
18+
import java.time.OffsetDateTime;
19+
import java.time.format.DateTimeFormatter;
20+
21+
import org.springframework.core.convert.converter.Converter;
22+
23+
/**
24+
* {@link Converter} implementation from {@link String} to {@link OffsetDateTime}.
25+
* <p>
26+
* This converter expects strings in the {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME}
27+
* format.
28+
*
29+
* @author Eunbin Son
30+
* @since 6.0.2
31+
*/
32+
public class StringToOffsetDateTimeConverter implements Converter<String, OffsetDateTime> {
33+
34+
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
35+
36+
@Override
37+
public OffsetDateTime convert(String source) {
38+
return OffsetDateTime.parse(source, FORMATTER);
39+
}
40+
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2026-present 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+
package org.springframework.batch.core.converter;
17+
18+
import java.time.ZonedDateTime;
19+
import java.time.format.DateTimeFormatter;
20+
21+
import org.springframework.core.convert.converter.Converter;
22+
23+
/**
24+
* {@link Converter} implementation from {@link String} to {@link ZonedDateTime}.
25+
* <p>
26+
* This converter expects strings in the {@link DateTimeFormatter#ISO_ZONED_DATE_TIME}
27+
* format.
28+
*
29+
* @author Eunbin Son
30+
* @since 6.0.2
31+
*/
32+
public class StringToZonedDateTimeConverter implements Converter<String, ZonedDateTime> {
33+
34+
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME;
35+
36+
@Override
37+
public ZonedDateTime convert(String source) {
38+
return ZonedDateTime.parse(source, FORMATTER);
39+
}
40+
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2026-present 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+
package org.springframework.batch.core.converter;
17+
18+
import java.time.ZonedDateTime;
19+
import java.time.format.DateTimeFormatter;
20+
21+
import org.springframework.core.convert.converter.Converter;
22+
23+
/**
24+
* {@link Converter} implementation from {@link ZonedDateTime} to {@link String}.
25+
* <p>
26+
* This converter formats dates according to the
27+
* {@link DateTimeFormatter#ISO_ZONED_DATE_TIME} format.
28+
*
29+
* @author Eunbin Son
30+
* @since 6.0.2
31+
*/
32+
public class ZonedDateTimeToStringConverter implements Converter<ZonedDateTime, String> {
33+
34+
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME;
35+
36+
@Override
37+
public String convert(ZonedDateTime source) {
38+
return source.format(FORMATTER);
39+
}
40+
41+
}

spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2006-2024 the original author or authors.
2+
* Copyright 2006-present the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,10 @@
1616
package org.springframework.batch.core.converter;
1717

1818
import java.time.LocalDate;
19+
import java.time.OffsetDateTime;
20+
import java.time.ZoneId;
21+
import java.time.ZoneOffset;
22+
import java.time.ZonedDateTime;
1923
import java.util.Properties;
2024

2125
import org.junit.jupiter.api.Test;
@@ -261,4 +265,79 @@ void testEmptyArgs() {
261265
assertTrue(props.parameters().isEmpty());
262266
}
263267

268+
@Test
269+
void testGetParametersWithZonedDateTime() {
270+
String zonedDateTime = "schedule.zonedDateTime=2023-12-25T10:30:00+09:00[Asia/Seoul],java.time.ZonedDateTime,true";
271+
String[] args = new String[] { zonedDateTime };
272+
273+
JobParameters parameters = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "="));
274+
assertNotNull(parameters);
275+
JobParameter<?> parameter = parameters.getParameter("schedule.zonedDateTime");
276+
assertEquals(ZonedDateTime.class, parameter.type());
277+
ZonedDateTime expected = ZonedDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneId.of("Asia/Seoul"));
278+
assertEquals(expected, parameter.value());
279+
}
280+
281+
@Test
282+
void testGetParametersWithOffsetDateTime() {
283+
String offsetDateTime = "schedule.offsetDateTime=2023-12-25T10:30:00+09:00,java.time.OffsetDateTime,true";
284+
String[] args = new String[] { offsetDateTime };
285+
286+
JobParameters parameters = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "="));
287+
assertNotNull(parameters);
288+
JobParameter<?> parameter = parameters.getParameter("schedule.offsetDateTime");
289+
assertEquals(OffsetDateTime.class, parameter.type());
290+
OffsetDateTime expected = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.of("+09:00"));
291+
assertEquals(expected, parameter.value());
292+
}
293+
294+
@Test
295+
void testGetPropertiesWithZonedDateTime() {
296+
ZonedDateTime zonedDateTime = ZonedDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneId.of("Asia/Seoul"));
297+
JobParameters parameters = new JobParametersBuilder()
298+
.addJobParameter("schedule.zonedDateTime", zonedDateTime, ZonedDateTime.class, true)
299+
.toJobParameters();
300+
301+
Properties props = factory.getProperties(parameters);
302+
assertNotNull(props);
303+
assertEquals("2023-12-25T10:30:00+09:00[Asia/Seoul],java.time.ZonedDateTime,true",
304+
props.getProperty("schedule.zonedDateTime"));
305+
}
306+
307+
@Test
308+
void testGetPropertiesWithOffsetDateTime() {
309+
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.of("+09:00"));
310+
JobParameters parameters = new JobParametersBuilder()
311+
.addJobParameter("schedule.offsetDateTime", offsetDateTime, OffsetDateTime.class, true)
312+
.toJobParameters();
313+
314+
Properties props = factory.getProperties(parameters);
315+
assertNotNull(props);
316+
assertEquals("2023-12-25T10:30:00+09:00,java.time.OffsetDateTime,true",
317+
props.getProperty("schedule.offsetDateTime"));
318+
}
319+
320+
@Test
321+
void testRoundTripWithZonedDateTime() {
322+
String[] args = new String[] {
323+
"schedule.zonedDateTime=2023-12-25T10:30:00+09:00[Asia/Seoul],java.time.ZonedDateTime" };
324+
325+
JobParameters parameters = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "="));
326+
Properties props = factory.getProperties(parameters);
327+
assertNotNull(props);
328+
assertEquals("2023-12-25T10:30:00+09:00[Asia/Seoul],java.time.ZonedDateTime,true",
329+
props.getProperty("schedule.zonedDateTime"));
330+
}
331+
332+
@Test
333+
void testRoundTripWithOffsetDateTime() {
334+
String[] args = new String[] { "schedule.offsetDateTime=2023-12-25T10:30:00+09:00,java.time.OffsetDateTime" };
335+
336+
JobParameters parameters = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "="));
337+
Properties props = factory.getProperties(parameters);
338+
assertNotNull(props);
339+
assertEquals("2023-12-25T10:30:00+09:00,java.time.OffsetDateTime,true",
340+
props.getProperty("schedule.offsetDateTime"));
341+
}
342+
264343
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2026-present 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+
package org.springframework.batch.core.converter;
17+
18+
import java.time.OffsetDateTime;
19+
import java.time.ZoneOffset;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
import static org.junit.jupiter.api.Assertions.assertEquals;
24+
25+
/**
26+
* Test class for {@link OffsetDateTimeToStringConverter}.
27+
*
28+
* @author Eunbin Son
29+
*/
30+
class OffsetDateTimeToStringConverterTests {
31+
32+
private final OffsetDateTimeToStringConverter converter = new OffsetDateTimeToStringConverter();
33+
34+
@Test
35+
void testConvert() {
36+
// given
37+
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.of("+09:00"));
38+
39+
// when
40+
String converted = converter.convert(offsetDateTime);
41+
42+
// then
43+
assertEquals("2023-12-25T10:30:00+09:00", converted);
44+
}
45+
46+
@Test
47+
void testConvertWithUTC() {
48+
// given
49+
OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.UTC);
50+
51+
// when
52+
String converted = converter.convert(offsetDateTime);
53+
54+
// then
55+
assertEquals("2023-12-25T10:30:00Z", converted);
56+
}
57+
58+
}

0 commit comments

Comments
 (0)