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); + } + +}