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` 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..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 @@ -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; @@ -244,7 +245,11 @@ private void registerJava8TimeConversions() { // Java 8 time 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/JavaOffsetDateTimeToLocalDateTimeConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToLocalDateTimeConversion.java new file mode 100644 index 0000000000..987c4cc625 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaOffsetDateTimeToLocalDateTimeConversion.java @@ -0,0 +1,35 @@ +/* + * 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 JavaOffsetDateTimeToLocalDateTimeConversion extends SimpleConversion { + + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toLocalDateTime()"; + } + + @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/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..9a9275cede --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/JavaZonedDateTimeToLocalDateTimeConversion.java @@ -0,0 +1,36 @@ +/* + * 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 JavaZonedDateTimeToLocalDateTimeConversion extends SimpleConversion { + + @Override + protected String getToExpression(ConversionContext conversionContext) { + return ".toLocalDateTime()"; + + } + + @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 new file mode 100644 index 0000000000..ab17980e96 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/ZonedOffsetDateTimeToLocalDateTimeConversionTest.java @@ -0,0 +1,113 @@ +/* + * 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.Instant; +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 ) ); + } + + @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 ) ); + } + + @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 new file mode 100644 index 0000000000..18bd99825c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Source.java @@ -0,0 +1,48 @@ +/* + * 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; + private ZonedDateTime zonedDateTimeAsInstant; + private OffsetDateTime offsetDateTimeAsInstant; + + 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; + } + + 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/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapper.java new file mode 100644 index 0000000000..43bee5f77f --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapper.java @@ -0,0 +1,18 @@ +/* + * 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); + + Source toSource(Target target); +} 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..b43b9f6843 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/Target.java @@ -0,0 +1,48 @@ +/* + * 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.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; + } + + public void setOffsetDateTime(LocalDateTime offsetDateTime) { + this.offsetDateTime = offsetDateTime; + } + + public LocalDateTime getZonedDateTime() { + return zonedDateTime; + } + + 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 new file mode 100644 index 0000000000..71f9998364 --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/ap/test/conversion/java8time/zonedoffsetdatetimetolocaldatetimeconversion/SourceTargetMapperImpl.java @@ -0,0 +1,65 @@ +/* + * 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-23T21:55: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() ); + } + if ( source.getZonedDateTimeAsInstant() != null ) { + target.setZonedDateTimeAsInstant( source.getZonedDateTimeAsInstant().toInstant() ); + } + if ( source.getOffsetDateTimeAsInstant() != null ) { + target.setOffsetDateTimeAsInstant( source.getOffsetDateTimeAsInstant().toInstant() ); + } + + 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 ) ); + } + if ( target.getZonedDateTimeAsInstant() != null ) { + source.setZonedDateTimeAsInstant( target.getZonedDateTimeAsInstant().atZone( ZoneOffset.UTC ) ); + } + if ( target.getOffsetDateTimeAsInstant() != null ) { + source.setOffsetDateTimeAsInstant( target.getOffsetDateTimeAsInstant().atOffset( ZoneOffset.UTC ) ); + } + + return source; + } +} \ No newline at end of file