diff --git a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc
index 44c757b11..d371e310b 100644
--- a/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc
+++ b/documentation/src/main/asciidoc/mapstruct-reference-guide.asciidoc
@@ -944,6 +944,8 @@ public interface CarMapper {
* Between `java.time.ZonedDateTime`, `java.time.LocalDateTime`, `java.time.LocalDate`, `java.time.LocalTime` from Java 8 Date-Time package and `String`. A format string as understood by `java.text.SimpleDateFormat` can be specified via the `dateFormat` option (see above).
+* Between `java.time.Instant`, `java.time.Duration`, `java.time.Period` from Java 8 Date-Time package and `String` using the `parse` method in each class to map from `String` and using `toString` to map into `String`.
+
* Between `java.time.ZonedDateTime` from Java 8 Date-Time package and `java.util.Date` where, when mapping a `ZonedDateTime` from a given `Date`, the system default timezone is used.
* Between `java.time.LocalDateTime` from Java 8 Date-Time package and `java.util.Date` where timezone UTC is used as the timezone.
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 6f4db9316..9f0ad9748 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
@@ -9,10 +9,12 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.sql.Timestamp;
+import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
+import java.time.Period;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Currency;
@@ -171,8 +173,8 @@ public class Conversions {
registerToStringConversion( Boolean.class );
register( char.class, String.class, new CharToStringConversion() );
register( Character.class, String.class, new CharWrapperToStringConversion() );
- register( BigInteger.class, String.class, new BigIntegerToStringConversion( ) );
- register( BigDecimal.class, String.class, new BigDecimalToStringConversion( ) );
+ register( BigInteger.class, String.class, new BigIntegerToStringConversion() );
+ register( BigDecimal.class, String.class, new BigDecimalToStringConversion() );
registerJodaConversions();
@@ -217,6 +219,9 @@ public class Conversions {
register( LocalDate.class, String.class, new JavaLocalDateToStringConversion() );
register( LocalDateTime.class, String.class, new JavaLocalDateTimeToStringConversion() );
register( LocalTime.class, String.class, new JavaLocalTimeToStringConversion() );
+ register( Instant.class, String.class, new StaticParseToStringConversion() );
+ register( Period.class, String.class, new StaticParseToStringConversion() );
+ register( Duration.class, String.class, new StaticParseToStringConversion() );
// Java 8 to Date
register( ZonedDateTime.class, Date.class, new JavaZonedDateTimeToDateConversion() );
diff --git a/processor/src/main/java/org/mapstruct/ap/internal/conversion/StaticParseToStringConversion.java b/processor/src/main/java/org/mapstruct/ap/internal/conversion/StaticParseToStringConversion.java
new file mode 100644
index 000000000..15d7b4bcc
--- /dev/null
+++ b/processor/src/main/java/org/mapstruct/ap/internal/conversion/StaticParseToStringConversion.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.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;
+
+/**
+ * Handles conversion between a target type T and {@link String},
+ * where {@code T#parse(String)} and {@code T#toString} are inverse operations.
+ * The {@link ConversionContext#getTargetType()} is used as the from target type T.
+ */
+public class StaticParseToStringConversion extends SimpleConversion {
+
+ @Override
+ protected String getToExpression(ConversionContext conversionContext) {
+ return ".toString()";
+ }
+
+ @Override
+ protected String getFromExpression(ConversionContext conversionContext) {
+ return conversionContext.getTargetType().createReferenceName() + ".parse( )";
+ }
+
+ @Override
+ protected Set getFromConversionImportTypes(ConversionContext conversionContext) {
+ return Collections.asSet( conversionContext.getTargetType() );
+ }
+
+}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java
index 271a8d958..573e300bf 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Java8TimeConversionTest.java
@@ -5,12 +5,12 @@
*/
package org.mapstruct.ap.test.conversion.java8time;
-import static org.assertj.core.api.Assertions.assertThat;
-
+import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
+import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
@@ -23,6 +23,8 @@ import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
+import static org.assertj.core.api.Assertions.assertThat;
+
/**
* Tests for conversions to/from Java 8 date and time types.
*/
@@ -99,7 +101,7 @@ public class Java8TimeConversionTest {
Target target = new Target();
target.setZonedDateTime( dateTimeAsString );
ZonedDateTime sourceDateTime =
- ZonedDateTime.of( java.time.LocalDateTime.of( 2014, 1, 1, 0, 0 ), ZoneId.of( "UTC" ) );
+ ZonedDateTime.of( java.time.LocalDateTime.of( 2014, 1, 1, 0, 0 ), ZoneId.of( "UTC" ) );
Source src = SourceTargetMapper.INSTANCE.targetToSourceDateTimeMapped( target );
assertThat( src ).isNotNull();
@@ -112,7 +114,7 @@ public class Java8TimeConversionTest {
Target target = new Target();
target.setLocalDateTime( dateTimeAsString );
LocalDateTime sourceDateTime =
- LocalDateTime.of( 2014, 1, 1, 0, 0, 0 );
+ LocalDateTime.of( 2014, 1, 1, 0, 0, 0 );
Source src = SourceTargetMapper.INSTANCE.targetToSourceLocalDateTimeMapped( target );
assertThat( src ).isNotNull();
@@ -125,7 +127,7 @@ public class Java8TimeConversionTest {
Target target = new Target();
target.setLocalDate( dateTimeAsString );
LocalDate sourceDate =
- LocalDate.of( 2014, 1, 1 );
+ LocalDate.of( 2014, 1, 1 );
Source src = SourceTargetMapper.INSTANCE.targetToSourceLocalDateMapped( target );
assertThat( src ).isNotNull();
@@ -138,7 +140,7 @@ public class Java8TimeConversionTest {
Target target = new Target();
target.setLocalTime( dateTimeAsString );
LocalTime sourceTime =
- LocalTime.of( 0, 0 );
+ LocalTime.of( 0, 0 );
Source src = SourceTargetMapper.INSTANCE.targetToSourceLocalTimeMapped( target );
assertThat( src ).isNotNull();
@@ -169,13 +171,14 @@ public class Java8TimeConversionTest {
Source src = SourceTargetMapper.INSTANCE.targetToSource( target );
assertThat( src.getZonedDateTime() ).isEqualTo(
- ZonedDateTime.of(
- java.time.LocalDateTime.of(
- 2014,
- 1,
- 1,
- 0,
- 0 ), ZoneId.of( "UTC" ) ) );
+ ZonedDateTime.of(
+ java.time.LocalDateTime.of(
+ 2014,
+ 1,
+ 1,
+ 0,
+ 0
+ ), ZoneId.of( "UTC" ) ) );
assertThat( src.getLocalDateTime() ).isEqualTo( LocalDateTime.of( 2014, 1, 1, 0, 0 ) );
assertThat( src.getLocalDate() ).isEqualTo( LocalDate.of( 2014, 1, 1 ) );
assertThat( src.getLocalTime() ).isEqualTo( LocalTime.of( 0, 0 ) );
@@ -186,17 +189,17 @@ public class Java8TimeConversionTest {
Source source = new Source();
ZonedDateTime dateTime = ZonedDateTime.of( LocalDateTime.of( 2014, 1, 1, 0, 0 ), ZoneId.of( "UTC" ) );
source.setForCalendarConversion(
- dateTime );
+ dateTime );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target.getForCalendarConversion() ).isNotNull();
assertThat( target.getForCalendarConversion().getTimeZone() ).isEqualTo(
- TimeZone.getTimeZone(
- "UTC" ) );
+ TimeZone.getTimeZone(
+ "UTC" ) );
assertThat( target.getForCalendarConversion().get( Calendar.YEAR ) ).isEqualTo( dateTime.getYear() );
assertThat( target.getForCalendarConversion().get( Calendar.MONTH ) ).isEqualTo(
- dateTime.getMonthValue() - 1 );
+ dateTime.getMonthValue() - 1 );
assertThat( target.getForCalendarConversion().get( Calendar.DATE ) ).isEqualTo( dateTime.getDayOfMonth() );
assertThat( target.getForCalendarConversion().get( Calendar.MINUTE ) ).isEqualTo( dateTime.getMinute() );
assertThat( target.getForCalendarConversion().get( Calendar.HOUR ) ).isEqualTo( dateTime.getHour() );
@@ -212,7 +215,7 @@ public class Java8TimeConversionTest {
Source source = new Source();
ZonedDateTime dateTime = ZonedDateTime.of( LocalDateTime.of( 2014, 1, 1, 0, 0 ), ZoneId.of( "UTC" ) );
source.setForDateConversionWithZonedDateTime(
- dateTime );
+ dateTime );
Target target = SourceTargetMapper.INSTANCE.sourceToTargetDefaultMapping( source );
assertThat( target.getForDateConversionWithZonedDateTime() ).isNotNull();
@@ -319,4 +322,124 @@ public class Java8TimeConversionTest {
assertThat( source.getForSqlDateConversionWithLocalDate() ).isEqualTo( localDate );
}
+
+ @Test
+ public void testInstantToStringMapping() {
+ Source source = new Source();
+ source.setForInstantConversionWithString( Instant.ofEpochSecond( 42L ) );
+
+ Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
+ String periodString = target.getForInstantConversionWithString();
+ assertThat( periodString ).isEqualTo( "1970-01-01T00:00:42Z" );
+ }
+
+ @Test
+ public void testInstantToStringNullMapping() {
+ Source source = new Source();
+ source.setForInstantConversionWithString( null );
+
+ Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
+ String periodString = target.getForInstantConversionWithString();
+ assertThat( periodString ).isNull();
+ }
+
+ @Test
+ public void testStringToInstantMapping() {
+ Target target = new Target();
+ target.setForInstantConversionWithString( "1970-01-01T00:00:00.000Z" );
+
+ Source source = SourceTargetMapper.INSTANCE.targetToSource( target );
+ Instant instant = source.getForInstantConversionWithString();
+ assertThat( instant ).isEqualTo( Instant.EPOCH );
+ }
+
+ @Test
+ public void testStringToInstantNullMapping() {
+ Target target = new Target();
+ target.setForInstantConversionWithString( null );
+
+ Source source = SourceTargetMapper.INSTANCE.targetToSource( target );
+ Instant instant = source.getForInstantConversionWithString();
+ assertThat( instant ).isNull();
+ }
+
+ @Test
+ public void testPeriodToStringMapping() {
+ Source source = new Source();
+ source.setForPeriodConversionWithString( Period.ofDays( 42 ) );
+
+ Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
+ String periodString = target.getForPeriodConversionWithString();
+ assertThat( periodString ).isEqualTo( "P42D" );
+ }
+
+ @Test
+ public void testPeriodToStringNullMapping() {
+ Source source = new Source();
+ source.setForPeriodConversionWithString( null );
+
+ Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
+ String periodString = target.getForPeriodConversionWithString();
+ assertThat( periodString ).isNull();
+ }
+
+ @Test
+ public void testStringToPeriodMapping() {
+ Target target = new Target();
+ target.setForPeriodConversionWithString( "P1Y2M3D" );
+
+ Source source = SourceTargetMapper.INSTANCE.targetToSource( target );
+ Period period = source.getForPeriodConversionWithString();
+ assertThat( period ).isEqualTo( Period.of( 1, 2, 3 ) );
+ }
+
+ @Test
+ public void testStringToPeriodNullMapping() {
+ Target target = new Target();
+ target.setForPeriodConversionWithString( null );
+
+ Source source = SourceTargetMapper.INSTANCE.targetToSource( target );
+ Period period = source.getForPeriodConversionWithString();
+ assertThat( period ).isNull();
+ }
+
+ @Test
+ public void testDurationToStringMapping() {
+ Source source = new Source();
+ source.setForDurationConversionWithString( Duration.ofMinutes( 42L ) );
+
+ Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
+ String durationString = target.getForDurationConversionWithString();
+ assertThat( durationString ).isEqualTo( "PT42M" );
+ }
+
+ @Test
+ public void testDurationToStringNullMapping() {
+ Source source = new Source();
+ source.setForDurationConversionWithString( null );
+
+ Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
+ String durationString = target.getForDurationConversionWithString();
+ assertThat( durationString ).isNull();
+ }
+
+ @Test
+ public void testStringToDurationMapping() {
+ Target target = new Target();
+ target.setForDurationConversionWithString( "PT20.345S" );
+
+ Source source = SourceTargetMapper.INSTANCE.targetToSource( target );
+ Duration duration = source.getForDurationConversionWithString();
+ assertThat( duration ).isEqualTo( Duration.ofSeconds( 20L, 345000000L ) );
+ }
+
+ @Test
+ public void testStringToDurationNullMapping() {
+ Target target = new Target();
+ target.setForDurationConversionWithString( null );
+
+ Source source = SourceTargetMapper.INSTANCE.targetToSource( target );
+ Duration duration = source.getForDurationConversionWithString();
+ assertThat( duration ).isNull();
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Source.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Source.java
index 886297170..91298617c 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Source.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Source.java
@@ -5,10 +5,12 @@
*/
package org.mapstruct.ap.test.conversion.java8time;
+import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
+import java.time.Period;
import java.time.ZonedDateTime;
/**
@@ -36,6 +38,12 @@ public class Source {
private Instant forDateConversionWithInstant;
+ private Instant forInstantConversionWithString;
+
+ private Period forPeriodConversionWithString;
+
+ private Duration forDurationConversionWithString;
+
public ZonedDateTime getZonedDateTime() {
return zonedDateTime;
}
@@ -115,4 +123,28 @@ public class Source {
public void setForDateConversionWithInstant(Instant forDateConversionWithInstant) {
this.forDateConversionWithInstant = forDateConversionWithInstant;
}
+
+ public Instant getForInstantConversionWithString() {
+ return forInstantConversionWithString;
+ }
+
+ public void setForInstantConversionWithString(Instant forInstantConversionWithString) {
+ this.forInstantConversionWithString = forInstantConversionWithString;
+ }
+
+ public Period getForPeriodConversionWithString() {
+ return forPeriodConversionWithString;
+ }
+
+ public void setForPeriodConversionWithString(Period forPeriodConversionWithString) {
+ this.forPeriodConversionWithString = forPeriodConversionWithString;
+ }
+
+ public Duration getForDurationConversionWithString() {
+ return forDurationConversionWithString;
+ }
+
+ public void setForDurationConversionWithString(Duration forDurationConversionWithString) {
+ this.forDurationConversionWithString = forDurationConversionWithString;
+ }
}
diff --git a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Target.java b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Target.java
index eac1603d4..188a6d0e2 100644
--- a/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Target.java
+++ b/processor/src/test/java/org/mapstruct/ap/test/conversion/java8time/Target.java
@@ -33,6 +33,12 @@ public class Target {
private Date forDateConversionWithInstant;
+ private String forInstantConversionWithString;
+
+ private String forPeriodConversionWithString;
+
+ private String forDurationConversionWithString;
+
public String getZonedDateTime() {
return zonedDateTime;
}
@@ -112,4 +118,28 @@ public class Target {
public void setForDateConversionWithInstant(Date forDateConversionWithInstant) {
this.forDateConversionWithInstant = forDateConversionWithInstant;
}
+
+ public String getForInstantConversionWithString() {
+ return forInstantConversionWithString;
+ }
+
+ public void setForInstantConversionWithString(String forInstantConversionWithString) {
+ this.forInstantConversionWithString = forInstantConversionWithString;
+ }
+
+ public String getForPeriodConversionWithString() {
+ return forPeriodConversionWithString;
+ }
+
+ public void setForPeriodConversionWithString(String forPeriodConversionWithString) {
+ this.forPeriodConversionWithString = forPeriodConversionWithString;
+ }
+
+ public String getForDurationConversionWithString() {
+ return forDurationConversionWithString;
+ }
+
+ public void setForDurationConversionWithString(String forDurationConversionWithString) {
+ this.forDurationConversionWithString = forDurationConversionWithString;
+ }
}