From d985b9c860c531ed3b1cc0e96afcb67c56a6d4b8 Mon Sep 17 00:00:00 2001 From: takch02 Date: Sun, 3 May 2026 13:51:54 +0900 Subject: [PATCH 1/6] #4027 Add support for implicit conversion between ZonedDateTime/OffsetDateTime and LocalDateTime --- .../ap/internal/conversion/Conversions.java | 13 +++++ ...fsetDateTimeToLocalDateTimeConversion.java | 23 ++++++++ ...onedDateTimeToLocalDateTimeConversion.java | 24 +++++++++ ...DateTimeToLocalDateTimeConversionTest.java | 52 +++++++++++++++++++ .../Source.java | 30 +++++++++++ .../SourceTargetMapper.java | 16 ++++++ .../Target.java | 29 +++++++++++ 7 files changed, 187 insertions(+) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToLocalDateTimeConversion.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToLocalDateTimeConversion.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Source.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Target.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index c29ad5141e..6d9507731b 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -16,6 +16,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.OffsetDateTime; import java.time.Period; import java.time.ZonedDateTime; import java.util.Calendar; @@ -245,6 +246,11 @@ private void registerJava8TimeConversions() { // Java 8 time register( LocalDateTime.class, LocalDate.class, new JavaLocalDateTimeToLocalDateConversion() ); + registerUnidirectional( ZonedDateTime.class, LocalDateTime.class, + new JavaZonedDateTimeToLocalDateTimeConversion() ); + registerUnidirectional( OffsetDateTime.class, LocalDateTime.class, + new JavaOffsetDateTimeToLocalDateTimeConversion() ); + } private void registerJavaTimeSqlConversions() { @@ -334,6 +340,13 @@ private void register(String sourceTypeName, Class targetClass, ConversionPro conversions.put( new Key( targetType, sourceType ), inverse( conversion ) ); } + private void registerUnidirectional(Class sourceClass, Class targetClass, ConversionProvider conversion) { + Type sourceType = typeFactory.getType( sourceClass ); + Type targetType = typeFactory.getType( targetClass ); + + conversions.put( new Key( sourceType, targetType ), conversion ); + } + public ConversionProvider getConversion(Type sourceType, Type targetType) { if ( sourceType.isOptionalType() ) { if ( targetType.isOptionalType() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToLocalDateTimeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToLocalDateTimeConversion.java new file mode 100644 index 0000000000..dfca488200 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToLocalDateTimeConversion.java @@ -0,0 +1,23 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.internal.conversion; + +import org.mapstruct.ap.internal.model.common.ConversionContext; + +public class JavaOffsetDateTimeToLocalDateTimeConversion extends SimpleConversion { + + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toLocalDateTime()"; + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + throw new UnsupportedOperationException( + "Mapping from LocalDateTime to OffsetDateTime is not supported - Offset information is required" + ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToLocalDateTimeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToLocalDateTimeConversion.java new file mode 100644 index 0000000000..f6794f67f3 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToLocalDateTimeConversion.java @@ -0,0 +1,24 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.internal.conversion; + +import org.mapstruct.ap.internal.model.common.ConversionContext; + +public class JavaZonedDateTimeToLocalDateTimeConversion extends SimpleConversion { + + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toLocalDateTime()"; + + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + throw new UnsupportedOperationException( + "Mapping from LocalDateTime to ZonedDateTime is not supported - Zone information is required" + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java new file mode 100644 index 0000000000..f99deaa420 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java @@ -0,0 +1,52 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.conversion.java8time; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion.Source; +import org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion.SourceTargetMapper; +import org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion.Target; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.ProcessorTest; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.GeneratedSource; + +import static org.assertj.core.api.Assertions.assertThat; + +@WithClasses({ Source.class, Target.class, SourceTargetMapper.class }) +@IssueKey("4027") +public class ZonedOffsetDateTimeToLocalDateTimeConversionTest { + + @RegisterExtension + GeneratedSource generatedSource = new GeneratedSource() + .addComparisonToFixtureFor( SourceTargetMapper.class ); + + @ProcessorTest + public void testZonedDateTimeToLocalDateTimeMapping() { + ZonedDateTime zonedDateTime = ZonedDateTime.of( 2024, 1, 1, 12, 30, 0, 0, ZoneId.of( "UTC" ) ); + Source source = new Source(); + source.setZonedDateTime( zonedDateTime ); + Target target = SourceTargetMapper.INSTANCE.toTarget( source ); + assertThat( target.getZonedDateTime() ) + .isEqualTo( LocalDateTime.of( 2024, 1, 1, 12, 30, 0 ) ); + } + + @ProcessorTest + public void testOffsetDateTimeToLocalDateTimeMapping() { + OffsetDateTime offsetDateTime = OffsetDateTime.of( 2024, 1, 1, 12, 30, 0, 0, ZoneOffset.UTC ); + Source source = new Source(); + source.setOffsetDateTime( offsetDateTime ); + Target target = SourceTargetMapper.INSTANCE.toTarget( source ); + assertThat( target.getOffsetDateTime() ) + .isEqualTo( LocalDateTime.of( 2024, 1, 1, 12, 30, 0 ) ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Source.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Source.java new file mode 100644 index 0000000000..7d27a62153 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Source.java @@ -0,0 +1,30 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion; + +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; + +public class Source { + private ZonedDateTime zonedDateTime; + private OffsetDateTime offsetDateTime; + + public ZonedDateTime getZonedDateTime() { + return zonedDateTime; + } + + public void setZonedDateTime(ZonedDateTime zonedDateTime) { + this.zonedDateTime = zonedDateTime; + } + + public OffsetDateTime getOffsetDateTime() { + return offsetDateTime; + } + + public void setOffsetDateTime(OffsetDateTime offsetDateTime) { + this.offsetDateTime = offsetDateTime; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapper.java new file mode 100644 index 0000000000..461ec56d8d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapper.java @@ -0,0 +1,16 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SourceTargetMapper { + SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + + Target toTarget(Source source); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Target.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Target.java new file mode 100644 index 0000000000..e04a32bb80 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Target.java @@ -0,0 +1,29 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion; + +import java.time.LocalDateTime; + +public class Target { + private LocalDateTime zonedDateTime; + private LocalDateTime offsetDateTime; + + public LocalDateTime getOffsetDateTime() { + return offsetDateTime; + } + + public void setOffsetDateTime(LocalDateTime offsetDateTime) { + this.offsetDateTime = offsetDateTime; + } + + public LocalDateTime getZonedDateTime() { + return zonedDateTime; + } + + public void setZonedDateTime(LocalDateTime zonedDateTime) { + this.zonedDateTime = zonedDateTime; + } +} From f492c3bc8481d44a53cd512eee609080589f0ec2 Mon Sep 17 00:00:00 2001 From: takch02 Date: Sun, 3 May 2026 13:52:18 +0900 Subject: [PATCH 2/6] #4027 Add fixture for SourceTargetMapperImpl --- .../SourceTargetMapperImpl.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java new file mode 100644 index 0000000000..1bff3ca13f --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java @@ -0,0 +1,29 @@ +package org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion; + +import javax.annotation.processing.Generated; + +@Generated( + value = "org.mapstruct.ap.MappingProcessor", + date = "2026-05-03T13:46:42+0900", + comments = "version: , compiler: javac, environment: Java 25.0.2 (Homebrew)" +) +public class SourceTargetMapperImpl implements SourceTargetMapper { + + @Override + public Target toTarget(Source source) { + if ( source == null ) { + return null; + } + + Target target = new Target(); + + if ( source.getOffsetDateTime() != null ) { + target.setOffsetDateTime( source.getOffsetDateTime().toLocalDateTime() ); + } + if ( source.getZonedDateTime() != null ) { + target.setZonedDateTime( source.getZonedDateTime().toLocalDateTime() ); + } + + return target; + } +} \ No newline at end of file From f0279a92dcc23b3f9a746fa535efb31d96813b9e Mon Sep 17 00:00:00 2001 From: takch02 Date: Sun, 3 May 2026 14:59:48 +0900 Subject: [PATCH 3/6] #4027 Add license Header --- .../SourceTargetMapperImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java index 1bff3ca13f..4f8ccf6930 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java @@ -1,3 +1,8 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ package org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion; import javax.annotation.processing.Generated; From 7fe60881823e64cabd0fa8fca4f0cf633c4720d7 Mon Sep 17 00:00:00 2001 From: takch02 Date: Sat, 23 May 2026 11:29:34 +0900 Subject: [PATCH 4/6] #4027 Add support for implicit conversion between ZonedDateTime/OffsetDateTime and LocalDateTime --- .../ap/internal/conversion/Conversions.java | 15 ++--------- ...fsetDateTimeToLocalDateTimeConversion.java | 16 ++++++++++-- ...onedDateTimeToLocalDateTimeConversion.java | 16 ++++++++++-- ...DateTimeToLocalDateTimeConversionTest.java | 20 ++++++++++++++ .../SourceTargetMapper.java | 2 ++ .../SourceTargetMapperImpl.java | 26 ++++++++++++++----- 6 files changed, 72 insertions(+), 23 deletions(-) diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index 6d9507731b..c6a8a96197 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -245,12 +245,8 @@ private void registerJava8TimeConversions() { // Java 8 time register( LocalDateTime.class, LocalDate.class, new JavaLocalDateTimeToLocalDateConversion() ); - - registerUnidirectional( ZonedDateTime.class, LocalDateTime.class, - new JavaZonedDateTimeToLocalDateTimeConversion() ); - registerUnidirectional( OffsetDateTime.class, LocalDateTime.class, - new JavaOffsetDateTimeToLocalDateTimeConversion() ); - + register( ZonedDateTime.class, LocalDateTime.class, new JavaZonedDateTimeToLocalDateTimeConversion() ); + register( OffsetDateTime.class, LocalDateTime.class, new JavaOffsetDateTimeToLocalDateTimeConversion() ); } private void registerJavaTimeSqlConversions() { @@ -340,13 +336,6 @@ private void register(String sourceTypeName, Class targetClass, ConversionPro conversions.put( new Key( targetType, sourceType ), inverse( conversion ) ); } - private void registerUnidirectional(Class sourceClass, Class targetClass, ConversionProvider conversion) { - Type sourceType = typeFactory.getType( sourceClass ); - Type targetType = typeFactory.getType( targetClass ); - - conversions.put( new Key( sourceType, targetType ), conversion ); - } - public ConversionProvider getConversion(Type sourceType, Type targetType) { if ( sourceType.isOptionalType() ) { if ( targetType.isOptionalType() ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToLocalDateTimeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToLocalDateTimeConversion.java index dfca488200..987c4cc625 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToLocalDateTimeConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToLocalDateTimeConversion.java @@ -5,7 +5,14 @@ */ package org.mapstruct.ap.internal.conversion; +import java.time.ZoneOffset; +import java.util.Set; + import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.Collections; + +import static org.mapstruct.ap.internal.conversion.ConversionUtils.zoneOffset; public class JavaOffsetDateTimeToLocalDateTimeConversion extends SimpleConversion { @@ -16,8 +23,13 @@ protected String getToExpression(ConversionContext conversionContext) { @Override protected String getFromExpression(ConversionContext conversionContext) { - throw new UnsupportedOperationException( - "Mapping from LocalDateTime to OffsetDateTime is not supported - Offset information is required" + return ".atOffset( " + zoneOffset( conversionContext ) + ".UTC )"; + } + + @Override + protected Set getFromConversionImportTypes(ConversionContext conversionContext) { + return Collections.asSet( + conversionContext.getTypeFactory().getType( ZoneOffset.class ) ); } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToLocalDateTimeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToLocalDateTimeConversion.java index f6794f67f3..9a9275cede 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToLocalDateTimeConversion.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToLocalDateTimeConversion.java @@ -5,7 +5,14 @@ */ package org.mapstruct.ap.internal.conversion; +import java.time.ZoneOffset; +import java.util.Set; + import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.Collections; + +import static org.mapstruct.ap.internal.conversion.ConversionUtils.zoneOffset; public class JavaZonedDateTimeToLocalDateTimeConversion extends SimpleConversion { @@ -17,8 +24,13 @@ protected String getToExpression(ConversionContext conversionContext) { @Override protected String getFromExpression(ConversionContext conversionContext) { - throw new UnsupportedOperationException( - "Mapping from LocalDateTime to ZonedDateTime is not supported - Zone information is required" + return ".atZone( " + zoneOffset( conversionContext ) + ".UTC )"; + } + + @Override + protected Set getFromConversionImportTypes(ConversionContext conversionContext) { + return Collections.asSet( + conversionContext.getTypeFactory().getType( ZoneOffset.class ) ); } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java index f99deaa420..d23e452e87 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java @@ -49,4 +49,24 @@ public void testOffsetDateTimeToLocalDateTimeMapping() { assertThat( target.getOffsetDateTime() ) .isEqualTo( LocalDateTime.of( 2024, 1, 1, 12, 30, 0 ) ); } + + @ProcessorTest + public void testLocalDateTimeToZonedDateTimeMapping() { + LocalDateTime localDateTime = LocalDateTime.of( 2024, 1, 1, 12, 30, 0 ); + Target target = new Target(); + target.setZonedDateTime( localDateTime ); + Source source = SourceTargetMapper.INSTANCE.toSource( target ); + assertThat( source.getZonedDateTime() ) + .isEqualTo( ZonedDateTime.of( 2024, 1, 1, 12, 30, 0, 0, ZoneOffset.UTC ) ); + } + + @ProcessorTest + public void testLocalDateTimeToOffsetDateTimeMapping() { + LocalDateTime localDateTime = LocalDateTime.of( 2024, 1, 1, 12, 30, 0 ); + Target target = new Target(); + target.setOffsetDateTime( localDateTime ); + Source source = SourceTargetMapper.INSTANCE.toSource( target ); + assertThat( source.getOffsetDateTime() ) + .isEqualTo( OffsetDateTime.of( 2024, 1, 1, 12, 30, 0, 0, ZoneOffset.UTC ) ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapper.java index 461ec56d8d..43bee5f77f 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapper.java @@ -13,4 +13,6 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); Target toTarget(Source source); + + Source toSource(Target target); } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java index 4f8ccf6930..6a9ba55b5e 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java @@ -1,15 +1,11 @@ -/* - * Copyright MapStruct Authors. - * - * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 - */ package org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion; +import java.time.ZoneOffset; import javax.annotation.processing.Generated; @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2026-05-03T13:46:42+0900", + date = "2026-05-23T11:15:32+0900", comments = "version: , compiler: javac, environment: Java 25.0.2 (Homebrew)" ) public class SourceTargetMapperImpl implements SourceTargetMapper { @@ -31,4 +27,22 @@ public Target toTarget(Source source) { return target; } + + @Override + public Source toSource(Target target) { + if ( target == null ) { + return null; + } + + Source source = new Source(); + + if ( target.getZonedDateTime() != null ) { + source.setZonedDateTime( target.getZonedDateTime().atZone( ZoneOffset.UTC ) ); + } + if ( target.getOffsetDateTime() != null ) { + source.setOffsetDateTime( target.getOffsetDateTime().atOffset( ZoneOffset.UTC ) ); + } + + return source; + } } \ No newline at end of file From 5fda707aa964274442a285621bbf6acfacf0cf04 Mon Sep 17 00:00:00 2001 From: takch02 Date: Sat, 23 May 2026 22:06:21 +0900 Subject: [PATCH 5/6] #4027 Add support for conversion Instant/LocalDateTime, Instant/ZoneDateTime --- .../ap/internal/conversion/Conversions.java | 3 ++ ...JavaOffsetDateTimeToInstantConversion.java | 34 +++++++++++++++ .../JavaZonedDateTimeToInstantConversion.java | 34 +++++++++++++++ ...DateTimeToLocalDateTimeConversionTest.java | 41 +++++++++++++++++++ .../Source.java | 18 ++++++++ .../Target.java | 19 +++++++++ .../SourceTargetMapperImpl.java | 19 ++++++++- 7 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToInstantConversion.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToInstantConversion.java diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java index c6a8a96197..6e1f2c03aa 100755 --- a/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/Conversions.java @@ -247,6 +247,9 @@ private void registerJava8TimeConversions() { register( LocalDateTime.class, LocalDate.class, new JavaLocalDateTimeToLocalDateConversion() ); register( ZonedDateTime.class, LocalDateTime.class, new JavaZonedDateTimeToLocalDateTimeConversion() ); register( OffsetDateTime.class, LocalDateTime.class, new JavaOffsetDateTimeToLocalDateTimeConversion() ); + + register( ZonedDateTime.class, Instant.class, new JavaZonedDateTimeToInstantConversion() ); + register( OffsetDateTime.class, Instant.class, new JavaOffsetDateTimeToInstantConversion() ); } private void registerJavaTimeSqlConversions() { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToInstantConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToInstantConversion.java new file mode 100644 index 0000000000..9c5cb63990 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToInstantConversion.java @@ -0,0 +1,34 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.internal.conversion; + +import java.time.ZoneOffset; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.Collections; + +import static org.mapstruct.ap.internal.conversion.ConversionUtils.zoneOffset; + +public class JavaOffsetDateTimeToInstantConversion extends SimpleConversion { + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toInstant()"; + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + return ".atOffset( " + zoneOffset( conversionContext ) + ".UTC )"; + } + + @Override + protected Set getFromConversionImportTypes(ConversionContext conversionContext) { + return Collections.asSet( + conversionContext.getTypeFactory().getType( ZoneOffset.class ) + ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToInstantConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToInstantConversion.java new file mode 100644 index 0000000000..44cc68c2a7 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToInstantConversion.java @@ -0,0 +1,34 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.internal.conversion; + +import java.time.ZoneOffset; +import java.util.Set; + +import org.mapstruct.ap.internal.model.common.ConversionContext; +import org.mapstruct.ap.internal.model.common.Type; +import org.mapstruct.ap.internal.util.Collections; + +import static org.mapstruct.ap.internal.conversion.ConversionUtils.zoneOffset; + +public class JavaZonedDateTimeToInstantConversion extends SimpleConversion { + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toInstant()"; + } + + @Override + protected String getFromExpression(ConversionContext conversionContext) { + return ".atZone( " + zoneOffset( conversionContext ) + ".UTC )"; + } + + @Override + protected Set getFromConversionImportTypes(ConversionContext conversionContext) { + return Collections.asSet( + conversionContext.getTypeFactory().getType( ZoneOffset.class ) + ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java index d23e452e87..ab17980e96 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java @@ -5,6 +5,7 @@ */ package org.mapstruct.ap.test.conversion.java8time; +import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZoneId; @@ -69,4 +70,44 @@ public void testLocalDateTimeToOffsetDateTimeMapping() { assertThat( source.getOffsetDateTime() ) .isEqualTo( OffsetDateTime.of( 2024, 1, 1, 12, 30, 0, 0, ZoneOffset.UTC ) ); } + + @ProcessorTest + public void testZonedDateTimeToInstantMapping() { + ZonedDateTime zonedDateTime = ZonedDateTime.of( 2024, 1, 1, 12, 30, 0, 0, ZoneOffset.UTC ); + Source source = new Source(); + source.setZonedDateTimeAsInstant( zonedDateTime ); + Target target = SourceTargetMapper.INSTANCE.toTarget( source ); + assertThat( target.getZonedDateTimeAsInstant() ) + .isEqualTo( Instant.parse( "2024-01-01T12:30:00Z" ) ); + } + + @ProcessorTest + public void testOffsetDateTimeToInstantMapping() { + OffsetDateTime offsetDateTime = OffsetDateTime.of( 2024, 1, 1, 12, 30, 0, 0, ZoneOffset.UTC ); + Source source = new Source(); + source.setOffsetDateTimeAsInstant( offsetDateTime ); + Target target = SourceTargetMapper.INSTANCE.toTarget( source ); + assertThat( target.getOffsetDateTimeAsInstant() ) + .isEqualTo( Instant.parse( "2024-01-01T12:30:00Z" ) ); + } + + @ProcessorTest + public void testInstantToZonedDateTimeMapping() { + Instant instant = Instant.parse( "2024-01-01T12:30:00Z" ); + Target target = new Target(); + target.setZonedDateTimeAsInstant( instant ); + Source source = SourceTargetMapper.INSTANCE.toSource( target ); + assertThat( source.getZonedDateTimeAsInstant() ) + .isEqualTo( ZonedDateTime.of( 2024, 1, 1, 12, 30, 0, 0, ZoneOffset.UTC ) ); + } + + @ProcessorTest + public void testInstantToOffsetDateTimeMapping() { + Instant instant = Instant.parse( "2024-01-01T12:30:00Z" ); + Target target = new Target(); + target.setOffsetDateTimeAsInstant( instant ); + Source source = SourceTargetMapper.INSTANCE.toSource( target ); + assertThat( source.getOffsetDateTimeAsInstant() ) + .isEqualTo( OffsetDateTime.of( 2024, 1, 1, 12, 30, 0, 0, ZoneOffset.UTC ) ); + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Source.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Source.java index 7d27a62153..18bd99825c 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Source.java @@ -11,6 +11,8 @@ public class Source { private ZonedDateTime zonedDateTime; private OffsetDateTime offsetDateTime; + private ZonedDateTime zonedDateTimeAsInstant; + private OffsetDateTime offsetDateTimeAsInstant; public ZonedDateTime getZonedDateTime() { return zonedDateTime; @@ -27,4 +29,20 @@ public OffsetDateTime getOffsetDateTime() { public void setOffsetDateTime(OffsetDateTime offsetDateTime) { this.offsetDateTime = offsetDateTime; } + + public ZonedDateTime getZonedDateTimeAsInstant() { + return zonedDateTimeAsInstant; + } + + public void setZonedDateTimeAsInstant(ZonedDateTime zonedDateTimeAsInstant) { + this.zonedDateTimeAsInstant = zonedDateTimeAsInstant; + } + + public OffsetDateTime getOffsetDateTimeAsInstant() { + return offsetDateTimeAsInstant; + } + + public void setOffsetDateTimeAsInstant(OffsetDateTime offsetDateTimeAsInstant) { + this.offsetDateTimeAsInstant = offsetDateTimeAsInstant; + } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Target.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Target.java index e04a32bb80..b43b9f6843 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Target.java @@ -5,11 +5,14 @@ */ package org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion; +import java.time.Instant; import java.time.LocalDateTime; public class Target { private LocalDateTime zonedDateTime; private LocalDateTime offsetDateTime; + private Instant zonedDateTimeAsInstant; + private Instant offsetDateTimeAsInstant; public LocalDateTime getOffsetDateTime() { return offsetDateTime; @@ -26,4 +29,20 @@ public LocalDateTime getZonedDateTime() { public void setZonedDateTime(LocalDateTime zonedDateTime) { this.zonedDateTime = zonedDateTime; } + + public Instant getZonedDateTimeAsInstant() { + return zonedDateTimeAsInstant; + } + + public void setZonedDateTimeAsInstant(Instant zonedDateTimeAsInstant) { + this.zonedDateTimeAsInstant = zonedDateTimeAsInstant; + } + + public Instant getOffsetDateTimeAsInstant() { + return offsetDateTimeAsInstant; + } + + public void setOffsetDateTimeAsInstant(Instant offsetDateTimeAsInstant) { + this.offsetDateTimeAsInstant = offsetDateTimeAsInstant; + } } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java index 6a9ba55b5e..71f9998364 100644 --- a/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java @@ -1,3 +1,8 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ package org.mapstruct.ap.test.conversion.java8time.zonedoffsetdatetimetolocaldatetimeconversion; import java.time.ZoneOffset; @@ -5,7 +10,7 @@ @Generated( value = "org.mapstruct.ap.MappingProcessor", - date = "2026-05-23T11:15:32+0900", + date = "2026-05-23T21:55:42+0900", comments = "version: , compiler: javac, environment: Java 25.0.2 (Homebrew)" ) public class SourceTargetMapperImpl implements SourceTargetMapper { @@ -24,6 +29,12 @@ public Target toTarget(Source source) { if ( source.getZonedDateTime() != null ) { target.setZonedDateTime( source.getZonedDateTime().toLocalDateTime() ); } + if ( source.getZonedDateTimeAsInstant() != null ) { + target.setZonedDateTimeAsInstant( source.getZonedDateTimeAsInstant().toInstant() ); + } + if ( source.getOffsetDateTimeAsInstant() != null ) { + target.setOffsetDateTimeAsInstant( source.getOffsetDateTimeAsInstant().toInstant() ); + } return target; } @@ -42,6 +53,12 @@ public Source toSource(Target target) { if ( target.getOffsetDateTime() != null ) { source.setOffsetDateTime( target.getOffsetDateTime().atOffset( ZoneOffset.UTC ) ); } + if ( target.getZonedDateTimeAsInstant() != null ) { + source.setZonedDateTimeAsInstant( target.getZonedDateTimeAsInstant().atZone( ZoneOffset.UTC ) ); + } + if ( target.getOffsetDateTimeAsInstant() != null ) { + source.setOffsetDateTimeAsInstant( target.getOffsetDateTimeAsInstant().atOffset( ZoneOffset.UTC ) ); + } return source; } From db5859472ccb4deb321775dc67590438ab9bdcbd Mon Sep 17 00:00:00 2001 From: takch02 Date: Sun, 24 May 2026 09:15:00 +0900 Subject: [PATCH 6/6] #4027 Add documentation --- .../asciidoc/chapter-5-data-type-conversions.asciidoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc index d95e6c7a62..10c41506de 100644 --- a/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc +++ b/documentation/src/main/asciidoc/chapter-5-data-type-conversions.asciidoc @@ -109,6 +109,14 @@ public interface CarMapper { * Between `java.time.LocalDateTime` from Java 8 Date-Time package and `java.time.LocalDate` from the same package. +* Between `java.time.ZonedDateTime` from Java 8 Date-Time package and `java.time.LocalDateTime` from the same package. + +* Between `java.time.OffsetDateTime` from Java 8 Date-Time package and `java.time.LocalDateTime` from the same package. + +* Between `java.time.ZonedDateTime` from Java 8 Date-Time package and `java.time.Instant` from the same package. + +* Between `java.time.OffsetDateTime` from Java 8 Date-Time package and `java.time.Instant` from the same package. + * Between `java.time.ZonedDateTime` from Java 8 Date-Time package and `java.util.Calendar`. * Between `java.sql.Date` and `java.util.Date`