diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/ser/InstantSerializerBase.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/ser/InstantSerializerBase.java index 14a16d17..6279c3f0 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/ser/InstantSerializerBase.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/ser/InstantSerializerBase.java @@ -16,6 +16,8 @@ package com.fasterxml.jackson.datatype.jsr310.ser; +import static com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE; + import java.time.format.DateTimeFormatter; import java.time.temporal.Temporal; import java.util.function.ToIntFunction; @@ -130,10 +132,9 @@ protected String formatValue(T value, SerializerProvider provider) DateTimeFormatter formatter = (_formatter != null) ? _formatter : defaultFormat; if (formatter != null) { if (formatter.getZone() == null) { // timezone set if annotated on property - // 19-Oct-2020, tatu: As per [modules-java#188], only override with explicitly - // set timezone, to minimize change from pre-2.12. May need to further - // improve in future to maybe introduce more configuration. - if (provider.getConfig().hasExplicitTimeZone()) { + // If the user specified to use the context TimeZone explicitly, and the formatter provided doesn't contain a TZ + // Then we use the TZ specified in the objectMapper + if (provider.getConfig().hasExplicitTimeZone() && provider.isEnabled(WRITE_DATES_WITH_CONTEXT_TIME_ZONE)) { formatter = formatter.withZone(provider.getTimeZone().toZoneId()); } } diff --git a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/OffsetDateTimeSerTest.java b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/OffsetDateTimeSerTest.java index 981457a2..c607bade 100644 --- a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/OffsetDateTimeSerTest.java +++ b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/OffsetDateTimeSerTest.java @@ -1,12 +1,17 @@ package com.fasterxml.jackson.datatype.jsr310.ser; +import static org.junit.Assert.assertEquals; + import java.time.Instant; import java.time.OffsetDateTime; import java.time.ZoneId; +import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.Temporal; import java.util.TimeZone; +import org.junit.Test; + import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; @@ -14,10 +19,6 @@ import com.fasterxml.jackson.datatype.jsr310.MockObjectConfiguration; import com.fasterxml.jackson.datatype.jsr310.ModuleTestBase; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - public class OffsetDateTimeSerTest extends ModuleTestBase { @@ -233,4 +234,32 @@ public void testSerializationWithTypeInfoAndMapperTimeZone() throws Exception assertEquals("The value is not correct.", "[\"" + OffsetDateTime.class.getName() + "\",\"" + FORMATTER.format(date) + "\"]", value); } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOn() throws Exception { + OffsetDateTime date = OffsetDateTime.now(Z3); + String value = MAPPER.writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the ZoneId Z2 + assertEquals("The value is incorrect", "\"" + FORMATTER.format(date.atZoneSameInstant(Z2)) + "\"", value); + } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOff() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = MAPPER.writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the ZoneId Z3 + assertEquals("The value is incorrect", "\"" + FORMATTER.format(date) + "\"", value); + } } diff --git a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/ZonedDateTimeSerTest.java b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/ZonedDateTimeSerTest.java index b18fd687..a6dc0542 100644 --- a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/ZonedDateTimeSerTest.java +++ b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/ZonedDateTimeSerTest.java @@ -16,6 +16,10 @@ package com.fasterxml.jackson.datatype.jsr310.ser; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -26,24 +30,23 @@ import java.util.Locale; import java.util.TimeZone; +import org.junit.Test; + import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.datatype.jsr310.DecimalUtils; import com.fasterxml.jackson.datatype.jsr310.MockObjectConfiguration; import com.fasterxml.jackson.datatype.jsr310.ModuleTestBase; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - public class ZonedDateTimeSerTest extends ModuleTestBase { + private static final DateTimeFormatter FORMATTER_WITHOUT_ZONEID = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME; private static final ZoneId Z1 = ZoneId.of("America/Chicago"); @@ -277,6 +280,70 @@ public void testSerializationAsStringWithZoneIdOn() throws Exception { assertEquals("The value is incorrect.", "\"" + DateTimeFormatter.ISO_ZONED_DATE_TIME.format(date) + "\"", value); } + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOnAndACustomFormatter() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + // With a custom DateTimeFormatter without a ZoneId. + String value = newMapperBuilder().addModule( + new SimpleModule().addSerializer(new ZonedDateTimeSerializer(FORMATTER_WITHOUT_ZONEID))) + .build() + .writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the datetime of ZoneId Z2 + assertEquals("The value is incorrect", "\"" + date.withZoneSameInstant(Z2).format(FORMATTER_WITHOUT_ZONEID) + "\"", value); + } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOffAndACustomFormatter() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + // With a custom DateTimeFormatter without a Zone. + String value = newMapperBuilder().addModule( + new SimpleModule().addSerializer(new ZonedDateTimeSerializer(FORMATTER_WITHOUT_ZONEID))) + .build() + .writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the datetime of ZoneId Z3 + assertEquals("The value is incorrect", "\"" + date.format(FORMATTER_WITHOUT_ZONEID) + "\"", value); + } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOn() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = MAPPER.writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the ZoneId Z2 + assertEquals("The value is incorrect", "\"" + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(date.withZoneSameInstant(Z2)) + "\"", value); + } + + @Test + public void testSerializationAsStringWithDefaultTimeZoneAndContextTimeZoneOff() throws Exception { + ZonedDateTime date = ZonedDateTime.now(Z3); + String value = MAPPER.writer() + .with(TimeZone.getTimeZone(Z2)) + .without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID) + .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .without(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(date); + + // We expect to have the date written with the ZoneId Z3 + assertEquals("The value is incorrect", "\"" + DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(date) + "\"", value); + } + @Test public void testSerializationWithTypeInfo01() throws Exception {