diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/converter/DefaultJobParametersConverter.java b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/DefaultJobParametersConverter.java
index 9775ef7644..ed7bbf6572 100644
--- a/spring-batch-core/src/main/java/org/springframework/batch/core/converter/DefaultJobParametersConverter.java
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/DefaultJobParametersConverter.java
@@ -64,6 +64,10 @@
* {@link java.time.format.DateTimeFormatter#ISO_LOCAL_TIME} format
*
{@link java.time.LocalDateTime}: in the
* {@link java.time.format.DateTimeFormatter#ISO_LOCAL_DATE_TIME} format
+ * {@link java.time.ZonedDateTime}: in the
+ * {@link java.time.format.DateTimeFormatter#ISO_ZONED_DATE_TIME} format
+ * {@link java.time.OffsetDateTime}: in the
+ * {@link java.time.format.DateTimeFormatter#ISO_OFFSET_DATE_TIME} format
*
*
* @author Dave Syer
@@ -85,6 +89,10 @@ public DefaultJobParametersConverter() {
conversionService.addConverter(new StringToLocalTimeConverter());
conversionService.addConverter(new LocalDateTimeToStringConverter());
conversionService.addConverter(new StringToLocalDateTimeConverter());
+ conversionService.addConverter(new ZonedDateTimeToStringConverter());
+ conversionService.addConverter(new StringToZonedDateTimeConverter());
+ conversionService.addConverter(new OffsetDateTimeToStringConverter());
+ conversionService.addConverter(new StringToOffsetDateTimeConverter());
this.conversionService = conversionService;
}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/converter/OffsetDateTimeToStringConverter.java b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/OffsetDateTimeToStringConverter.java
new file mode 100644
index 0000000000..bb5317339e
--- /dev/null
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/OffsetDateTimeToStringConverter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.core.converter;
+
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.springframework.core.convert.converter.Converter;
+
+/**
+ * {@link Converter} implementation from {@link OffsetDateTime} to {@link String}.
+ *
+ * This converter formats dates according to the
+ * {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME} format.
+ *
+ * @author Eunbin Son
+ * @since 6.0.3
+ */
+public class OffsetDateTimeToStringConverter implements Converter {
+
+ private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+
+ @Override
+ public String convert(OffsetDateTime source) {
+ return source.format(FORMATTER);
+ }
+
+}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/converter/StringToOffsetDateTimeConverter.java b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/StringToOffsetDateTimeConverter.java
new file mode 100644
index 0000000000..dbb3b0f26e
--- /dev/null
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/StringToOffsetDateTimeConverter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.core.converter;
+
+import java.time.OffsetDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.springframework.core.convert.converter.Converter;
+
+/**
+ * {@link Converter} implementation from {@link String} to {@link OffsetDateTime}.
+ *
+ * This converter expects strings in the {@link DateTimeFormatter#ISO_OFFSET_DATE_TIME}
+ * format.
+ *
+ * @author Eunbin Son
+ * @since 6.0.3
+ */
+public class StringToOffsetDateTimeConverter implements Converter {
+
+ private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
+
+ @Override
+ public OffsetDateTime convert(String source) {
+ return OffsetDateTime.parse(source, FORMATTER);
+ }
+
+}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/converter/StringToZonedDateTimeConverter.java b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/StringToZonedDateTimeConverter.java
new file mode 100644
index 0000000000..8d509840ec
--- /dev/null
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/StringToZonedDateTimeConverter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.core.converter;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.springframework.core.convert.converter.Converter;
+
+/**
+ * {@link Converter} implementation from {@link String} to {@link ZonedDateTime}.
+ *
+ * This converter expects strings in the {@link DateTimeFormatter#ISO_ZONED_DATE_TIME}
+ * format.
+ *
+ * @author Eunbin Son
+ * @since 6.0.3
+ */
+public class StringToZonedDateTimeConverter implements Converter {
+
+ private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME;
+
+ @Override
+ public ZonedDateTime convert(String source) {
+ return ZonedDateTime.parse(source, FORMATTER);
+ }
+
+}
diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/converter/ZonedDateTimeToStringConverter.java b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/ZonedDateTimeToStringConverter.java
new file mode 100644
index 0000000000..d1b5997700
--- /dev/null
+++ b/spring-batch-core/src/main/java/org/springframework/batch/core/converter/ZonedDateTimeToStringConverter.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.core.converter;
+
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.springframework.core.convert.converter.Converter;
+
+/**
+ * {@link Converter} implementation from {@link ZonedDateTime} to {@link String}.
+ *
+ * This converter formats dates according to the
+ * {@link DateTimeFormatter#ISO_ZONED_DATE_TIME} format.
+ *
+ * @author Eunbin Son
+ * @since 6.0.3
+ */
+public class ZonedDateTimeToStringConverter implements Converter {
+
+ private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME;
+
+ @Override
+ public String convert(ZonedDateTime source) {
+ return source.format(FORMATTER);
+ }
+
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java
index 558163150a..74e926d5d7 100644
--- a/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/DefaultJobParametersConverterTests.java
@@ -16,6 +16,10 @@
package org.springframework.batch.core.converter;
import java.time.LocalDate;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
import java.util.Properties;
import org.junit.jupiter.api.Test;
@@ -261,4 +265,79 @@ void testEmptyArgs() {
assertTrue(props.parameters().isEmpty());
}
+ @Test
+ void testGetParametersWithZonedDateTime() {
+ String zonedDateTime = "schedule.zonedDateTime=2023-12-25T10:30:00+09:00[Asia/Seoul],java.time.ZonedDateTime,true";
+ String[] args = new String[] { zonedDateTime };
+
+ JobParameters parameters = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "="));
+ assertNotNull(parameters);
+ JobParameter> parameter = parameters.getParameter("schedule.zonedDateTime");
+ assertEquals(ZonedDateTime.class, parameter.type());
+ ZonedDateTime expected = ZonedDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneId.of("Asia/Seoul"));
+ assertEquals(expected, parameter.value());
+ }
+
+ @Test
+ void testGetParametersWithOffsetDateTime() {
+ String offsetDateTime = "schedule.offsetDateTime=2023-12-25T10:30:00+09:00,java.time.OffsetDateTime,true";
+ String[] args = new String[] { offsetDateTime };
+
+ JobParameters parameters = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "="));
+ assertNotNull(parameters);
+ JobParameter> parameter = parameters.getParameter("schedule.offsetDateTime");
+ assertEquals(OffsetDateTime.class, parameter.type());
+ OffsetDateTime expected = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.of("+09:00"));
+ assertEquals(expected, parameter.value());
+ }
+
+ @Test
+ void testGetPropertiesWithZonedDateTime() {
+ ZonedDateTime zonedDateTime = ZonedDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneId.of("Asia/Seoul"));
+ JobParameters parameters = new JobParametersBuilder()
+ .addJobParameter("schedule.zonedDateTime", zonedDateTime, ZonedDateTime.class, true)
+ .toJobParameters();
+
+ Properties props = factory.getProperties(parameters);
+ assertNotNull(props);
+ assertEquals("2023-12-25T10:30:00+09:00[Asia/Seoul],java.time.ZonedDateTime,true",
+ props.getProperty("schedule.zonedDateTime"));
+ }
+
+ @Test
+ void testGetPropertiesWithOffsetDateTime() {
+ OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.of("+09:00"));
+ JobParameters parameters = new JobParametersBuilder()
+ .addJobParameter("schedule.offsetDateTime", offsetDateTime, OffsetDateTime.class, true)
+ .toJobParameters();
+
+ Properties props = factory.getProperties(parameters);
+ assertNotNull(props);
+ assertEquals("2023-12-25T10:30:00+09:00,java.time.OffsetDateTime,true",
+ props.getProperty("schedule.offsetDateTime"));
+ }
+
+ @Test
+ void testRoundTripWithZonedDateTime() {
+ String[] args = new String[] {
+ "schedule.zonedDateTime=2023-12-25T10:30:00+09:00[Asia/Seoul],java.time.ZonedDateTime" };
+
+ JobParameters parameters = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "="));
+ Properties props = factory.getProperties(parameters);
+ assertNotNull(props);
+ assertEquals("2023-12-25T10:30:00+09:00[Asia/Seoul],java.time.ZonedDateTime,true",
+ props.getProperty("schedule.zonedDateTime"));
+ }
+
+ @Test
+ void testRoundTripWithOffsetDateTime() {
+ String[] args = new String[] { "schedule.offsetDateTime=2023-12-25T10:30:00+09:00,java.time.OffsetDateTime" };
+
+ JobParameters parameters = factory.getJobParameters(StringUtils.splitArrayElementsIntoProperties(args, "="));
+ Properties props = factory.getProperties(parameters);
+ assertNotNull(props);
+ assertEquals("2023-12-25T10:30:00+09:00,java.time.OffsetDateTime,true",
+ props.getProperty("schedule.offsetDateTime"));
+ }
+
}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/converter/OffsetDateTimeToStringConverterTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/OffsetDateTimeToStringConverterTests.java
new file mode 100644
index 0000000000..1a1239fb4b
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/OffsetDateTimeToStringConverterTests.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.core.converter;
+
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test class for {@link OffsetDateTimeToStringConverter}.
+ *
+ * @author Eunbin Son
+ */
+class OffsetDateTimeToStringConverterTests {
+
+ private final OffsetDateTimeToStringConverter converter = new OffsetDateTimeToStringConverter();
+
+ @Test
+ void testConvert() {
+ // given
+ OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.of("+09:00"));
+
+ // when
+ String converted = converter.convert(offsetDateTime);
+
+ // then
+ assertEquals("2023-12-25T10:30:00+09:00", converted);
+ }
+
+ @Test
+ void testConvertWithUTC() {
+ // given
+ OffsetDateTime offsetDateTime = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.UTC);
+
+ // when
+ String converted = converter.convert(offsetDateTime);
+
+ // then
+ assertEquals("2023-12-25T10:30:00Z", converted);
+ }
+
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/converter/StringToOffsetDateTimeConverterTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/StringToOffsetDateTimeConverterTests.java
new file mode 100644
index 0000000000..ca48b27668
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/StringToOffsetDateTimeConverterTests.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.core.converter;
+
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test class for {@link StringToOffsetDateTimeConverter}.
+ *
+ * @author Eunbin Son
+ */
+class StringToOffsetDateTimeConverterTests {
+
+ private final StringToOffsetDateTimeConverter converter = new StringToOffsetDateTimeConverter();
+
+ @Test
+ void testConvert() {
+ // given
+ String dateTime = "2023-12-25T10:30:00+09:00";
+
+ // when
+ OffsetDateTime converted = converter.convert(dateTime);
+
+ // then
+ OffsetDateTime expected = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.of("+09:00"));
+ assertEquals(expected, converted);
+ }
+
+ @Test
+ void testConvertWithUTC() {
+ // given
+ String dateTime = "2023-12-25T10:30:00Z";
+
+ // when
+ OffsetDateTime converted = converter.convert(dateTime);
+
+ // then
+ OffsetDateTime expected = OffsetDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneOffset.UTC);
+ assertEquals(expected, converted);
+ }
+
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/converter/StringToZonedDateTimeConverterTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/StringToZonedDateTimeConverterTests.java
new file mode 100644
index 0000000000..b410bfc912
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/StringToZonedDateTimeConverterTests.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2023-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.core.converter;
+
+import java.time.ZonedDateTime;
+import java.time.ZoneId;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test class for {@link StringToZonedDateTimeConverter}.
+ *
+ * @author Eunbin Son
+ */
+class StringToZonedDateTimeConverterTests {
+
+ private final StringToZonedDateTimeConverter converter = new StringToZonedDateTimeConverter();
+
+ @Test
+ void testConvert() {
+ // given
+ String dateTime = "2023-12-25T10:30:00+09:00[Asia/Seoul]";
+
+ // when
+ ZonedDateTime converted = converter.convert(dateTime);
+
+ // then
+ ZonedDateTime expected = ZonedDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneId.of("Asia/Seoul"));
+ assertEquals(expected, converted);
+ }
+
+ @Test
+ void testConvertWithUTC() {
+ // given
+ String dateTime = "2023-12-25T10:30:00Z[UTC]";
+
+ // when
+ ZonedDateTime converted = converter.convert(dateTime);
+
+ // then
+ ZonedDateTime expected = ZonedDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneId.of("UTC"));
+ assertEquals(expected, converted);
+ }
+
+}
diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/converter/ZonedDateTimeToStringConverterTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/ZonedDateTimeToStringConverterTests.java
new file mode 100644
index 0000000000..1bcdbd88bf
--- /dev/null
+++ b/spring-batch-core/src/test/java/org/springframework/batch/core/converter/ZonedDateTimeToStringConverterTests.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.batch.core.converter;
+
+import java.time.ZonedDateTime;
+import java.time.ZoneId;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test class for {@link ZonedDateTimeToStringConverter}.
+ *
+ * @author Eunbin Son
+ */
+class ZonedDateTimeToStringConverterTests {
+
+ private final ZonedDateTimeToStringConverter converter = new ZonedDateTimeToStringConverter();
+
+ @Test
+ void testConvert() {
+ // given
+ ZonedDateTime zonedDateTime = ZonedDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneId.of("Asia/Seoul"));
+
+ // when
+ String converted = converter.convert(zonedDateTime);
+
+ // then
+ assertEquals("2023-12-25T10:30:00+09:00[Asia/Seoul]", converted);
+ }
+
+ @Test
+ void testConvertWithUTC() {
+ // given
+ ZonedDateTime zonedDateTime = ZonedDateTime.of(2023, 12, 25, 10, 30, 0, 0, ZoneId.of("UTC"));
+
+ // when
+ String converted = converter.convert(zonedDateTime);
+
+ // then
+ assertEquals("2023-12-25T10:30:00Z[UTC]", converted);
+ }
+
+}