From c510a92abb86d88346f35487a9a4e9f4255608da Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Sun, 4 May 2025 20:40:14 +0900 Subject: [PATCH 1/7] Add test for 98 --- .../JsonFormatTimeZoneWithPattern98Test.java | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java new file mode 100644 index 0000000..dd322d9 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java @@ -0,0 +1,56 @@ +package com.fasterxml.jackson.datatype.joda.ser; + +import java.util.TimeZone; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.joda.JodaTestBase; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class JsonFormatTimeZoneWithPattern98Test extends JodaTestBase +{ + static class Wrapper { + @JsonFormat( + shape = JsonFormat.Shape.STRING, + pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS ZZZ", + timezone = "Europe/Budapest" // +01:00 in winter + ) + public DateTime value; + + Wrapper(DateTime v) { value = v; } + } + + private final ObjectMapper MAPPER = mapperWithModuleBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + + @Test + public void patternShouldNotEraseTimeZone() + throws Exception + { + // Explicity set with Timezone Europe/Budapest + _testSerializationOutput( + new DateTime(2018, 1, 1, 12, 1, 2, 3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Budapest"))) + ); + // Using JsonFormat + _testSerializationOutput( + new DateTime(2018, 1, 1, 12, 1, 2, 3) + ); + } + + private void _testSerializationOutput( + DateTime dateTime + ) throws Exception + { + String actual = MAPPER.writeValueAsString(new Wrapper(dateTime)); + String exp = "{\"value\":\"2018-01-01T12:01:02.003 Europe/Budapest\"}"; + assertEquals(exp, actual); + } + +} \ No newline at end of file From 27d59825ed00adb750870c701fcc4be331c93b56 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Mon, 5 May 2025 00:00:12 +0900 Subject: [PATCH 2/7] Fix #98 --- .../joda/cfg/JacksonJodaDateFormat.java | 6 +++++ .../JsonFormatTimeZoneWithPattern98Test.java | 23 +++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java b/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java index 60ed3b4..90a4fdc 100644 --- a/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java +++ b/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java @@ -162,6 +162,12 @@ public JacksonJodaDateFormat withFormat(String format) { if (_locale != null) { formatter = formatter.withLocale(_locale); } + + // [datatype-joda#98] Since 2.19.1, fix `@JsonFormat.timezone` not taking effect + // [If a timezone was explicitly set earlier, retain it on the new formatter + if (_explicitTimezone && _jdkTimezone != null) { + formatter = formatter.withZone(DateTimeZone.forTimeZone(_jdkTimezone)); + } return new JacksonJodaDateFormat(this, formatter); } diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java index dd322d9..ff6ab6a 100644 --- a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java +++ b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java @@ -2,13 +2,14 @@ import java.util.TimeZone; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.junit.jupiter.api.Test; + import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.joda.JodaTestBase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -33,23 +34,25 @@ static class Wrapper { public void patternShouldNotEraseTimeZone() throws Exception { - // Explicity set with Timezone Europe/Budapest + // Explicity set with Timezone _testSerializationOutput( + /* expectedHour */ "12", new DateTime(2018, 1, 1, 12, 1, 2, 3, DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Budapest"))) ); - // Using JsonFormat + // Using @JsonFormat _testSerializationOutput( - new DateTime(2018, 1, 1, 12, 1, 2, 3) + /* expectedHour */ "13", + new DateTime(2018, 1, 1, 12, 1, 2, 3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC"))) ); } - private void _testSerializationOutput( - DateTime dateTime - ) throws Exception + private void _testSerializationOutput(String expectedHour, DateTime dateTime) + throws Exception { String actual = MAPPER.writeValueAsString(new Wrapper(dateTime)); - String exp = "{\"value\":\"2018-01-01T12:01:02.003 Europe/Budapest\"}"; + String exp = "{\"value\":\"2018-01-01T"+expectedHour+":01:02.003 Europe/Budapest\"}"; assertEquals(exp, actual); } From 43e95a39293c09d855ef25593a1c68c3e72cde78 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Mon, 5 May 2025 00:10:41 +0900 Subject: [PATCH 3/7] Use parameterized type --- .../JsonFormatTimeZoneWithPattern98Test.java | 38 +++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java index ff6ab6a..b314431 100644 --- a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java +++ b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java @@ -13,17 +13,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class JsonFormatTimeZoneWithPattern98Test extends JodaTestBase -{ - static class Wrapper { +public class JsonFormatTimeZoneWithPattern98Test extends JodaTestBase { + static class Wrapper { @JsonFormat( - shape = JsonFormat.Shape.STRING, + shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS ZZZ", timezone = "Europe/Budapest" // +01:00 in winter ) - public DateTime value; + public T value; + + Wrapper(T v) { + value = v; + } - Wrapper(DateTime v) { value = v; } } private final ObjectMapper MAPPER = mapperWithModuleBuilder() @@ -32,27 +34,23 @@ static class Wrapper { @Test public void patternShouldNotEraseTimeZone() - throws Exception - { + throws Exception { // Explicity set with Timezone _testSerializationOutput( /* expectedHour */ "12", - new DateTime(2018, 1, 1, 12, 1, 2, 3, - DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Budapest"))) - ); + new Wrapper(new DateTime(2018, 1, 1, 12, 1, 2, 3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Budapest"))))); // Using @JsonFormat _testSerializationOutput( - /* expectedHour */ "13", - new DateTime(2018, 1, 1, 12, 1, 2, 3, - DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC"))) - ); + /* expectedHour */ "13", + new Wrapper(new DateTime(2018, 1, 1, 12, 1, 2, 3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC"))))); } - private void _testSerializationOutput(String expectedHour, DateTime dateTime) - throws Exception - { - String actual = MAPPER.writeValueAsString(new Wrapper(dateTime)); - String exp = "{\"value\":\"2018-01-01T"+expectedHour+":01:02.003 Europe/Budapest\"}"; + private void _testSerializationOutput(String expectedHour, Wrapper wrapper) + throws Exception { + String actual = MAPPER.writeValueAsString(wrapper); + String exp = "{\"value\":\"2018-01-01T" + expectedHour + ":01:02.003 Europe/Budapest\"}"; assertEquals(exp, actual); } From 167dc613ebcb713ae4cbc6e06df432f79ddb65cb Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Mon, 5 May 2025 00:17:51 +0900 Subject: [PATCH 4/7] With other date types --- .../JsonFormatTimeZoneWithPattern98Test.java | 55 +++++++++++++------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java index b314431..3320ff6 100644 --- a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java +++ b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java @@ -4,6 +4,9 @@ import org.joda.time.DateTime; import org.joda.time.DateTimeZone; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.joda.time.LocalTime; import org.junit.jupiter.api.Test; import com.fasterxml.jackson.annotation.JsonFormat; @@ -27,31 +30,49 @@ static class Wrapper { } } - private final ObjectMapper MAPPER = mapperWithModuleBuilder() .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .build(); @Test - public void patternShouldNotEraseTimeZone() - throws Exception { - // Explicity set with Timezone - _testSerializationOutput( - /* expectedHour */ "12", - new Wrapper(new DateTime(2018, 1, 1, 12, 1, 2, 3, - DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Budapest"))))); - // Using @JsonFormat - _testSerializationOutput( - /* expectedHour */ "13", - new Wrapper(new DateTime(2018, 1, 1, 12, 1, 2, 3, - DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC"))))); + public void patternShouldNotEraseTimeZone() throws Exception + { + // DateTime already in Europe/Budapest zone (no shift) + _testSerialization( + "{\"value\":\"2018-01-01T12:01:02.003 Europe/Budapest\"}", + new Wrapper<>(new DateTime(2018,1,1,12,1,2,3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Budapest")))) + ); + + // DateTime in UTC, should shift +1h + _testSerialization( + "{\"value\":\"2018-01-01T13:01:02.003 Europe/Budapest\"}", + new Wrapper<>(new DateTime(2018,1,1,12,1,2,3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC")))) + ); + + // LocalDate + _testSerialization( + "{\"value\":\"2018-01-01T��:��:��.000 \"}", + new Wrapper<>(new LocalDate(2018,1,1)) + ); + + // LocalTime + _testSerialization( + "{\"value\":\"����-��-��T12:01:02.003 \"}", + new Wrapper<>(new LocalTime(12,1,2,3)) + ); + + // LocalDateTime + _testSerialization( + "{\"value\":\"2018-01-01T12:01:02.003 \"}", + new Wrapper<>(new LocalDateTime(2018,1,1,12,1,2,3)) + ); } - private void _testSerializationOutput(String expectedHour, Wrapper wrapper) - throws Exception { + private void _testSerialization(String expectedJson, Object wrapper) throws Exception { String actual = MAPPER.writeValueAsString(wrapper); - String exp = "{\"value\":\"2018-01-01T" + expectedHour + ":01:02.003 Europe/Budapest\"}"; - assertEquals(exp, actual); + assertEquals(expectedJson, actual); } } \ No newline at end of file From d676d834f961832477fc45e045f3384be2baf77e Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Mon, 5 May 2025 01:04:33 +0900 Subject: [PATCH 5/7] wip --- .../joda/cfg/JacksonJodaDateFormat.java | 22 +++++++++ .../DateTimeOwnZoneSerialization92Test.java | 48 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeOwnZoneSerialization92Test.java diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java b/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java index 90a4fdc..e961c92 100644 --- a/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java +++ b/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java @@ -254,6 +254,28 @@ public DateTimeFormatter createFormatter(SerializerProvider ctxt) return formatter; } + /** + * Creates a formatter with the specified timezone from the value if any. + * + * [dataformat-joda#92] DateTime serialization result is not same as Java 8 ZonedDateTime + * + * @since 2.19.1 + */ + public DateTimeFormatter createFormatter(SerializerProvider ctxt, DateTimeZone valueTimeZone) + { + DateTimeFormatter formatter = createFormatterWithLocale(ctxt); + if (!_explicitTimezone) { + TimeZone tz = ctxt.getTimeZone(); + if ((tz != null) && !tz.equals(_jdkTimezone)) { + formatter = formatter.withZone(DateTimeZone.forTimeZone(tz)); + } + } + if (valueTimeZone != null && !valueTimeZone.equals(_jdkTimezone)) { + formatter = formatter.withZone(valueTimeZone); + } + return formatter; + } + public DateTimeFormatter createFormatterWithLocale(SerializerProvider ctxt) { DateTimeFormatter formatter = _formatter; diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeOwnZoneSerialization92Test.java b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeOwnZoneSerialization92Test.java new file mode 100644 index 0000000..25f4f3f --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeOwnZoneSerialization92Test.java @@ -0,0 +1,48 @@ +package com.fasterxml.jackson.datatype.joda.ser; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.joda.JodaTestBase; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Reproduces jackson-datatype-joda#92: + * DateTime loses its own zone during serialization (normalised to UTC), + * so the JSON differs from Java 8 ZonedDateTime output. + * + * Expected (zone kept): "2017‑01‑01T01:01:01.000+08:00" + * Actual (current bug): "2016‑12‑31T17:01:01.000Z" + */ +public class DateTimeOwnZoneSerialization92Test + extends JodaTestBase +{ + // Wrapper matches style used in other failing tests + static class Wrapper { + @JsonFormat(shape = JsonFormat.Shape.STRING, + pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS ZZZ") // default ISO pattern + public DateTime value; + Wrapper(DateTime v) { value = v; } + } + + private final ObjectMapper MAPPER = mapperWithModuleBuilder() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // string mode + .build(); + + @Test + public void dateTimeShouldRetainItsOwnZone() throws Exception { + DateTime europe = new DateTime( + 2017, 1, 1, 12, 1, 1, 0, + DateTimeZone.forID("Europe/Budapest") // UTC+8 + ); + + String actual = MAPPER.writeValueAsString(new Wrapper(europe)); + + // This assertion FAILS today, demonstrating the bug + assertEquals("{\"value\":\"2017-01-01T11:01:01.000 Europe/Budapest\"}", actual); + } +} \ No newline at end of file From 998bc4561051273645daef903849327dd95da9a6 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Mon, 5 May 2025 01:13:04 +0900 Subject: [PATCH 6/7] Add more tests regarding what should TimeZone indication --- .../JsonFormatTimeZoneWithPattern98Test.java | 83 ++++++++++++++----- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java index 3320ff6..bfd677e 100644 --- a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java +++ b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JsonFormatTimeZoneWithPattern98Test.java @@ -17,57 +17,98 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class JsonFormatTimeZoneWithPattern98Test extends JodaTestBase { - static class Wrapper { + static class Wrapper3Z { @JsonFormat( shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS ZZZ", timezone = "Europe/Budapest" // +01:00 in winter ) public T value; + Wrapper3Z(T v) { value = v; } + } - Wrapper(T v) { - value = v; - } + static class Wrapper2Z { + @JsonFormat( + shape = JsonFormat.Shape.STRING, + pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS ZZ", + timezone = "Europe/Budapest" + ) + public T value; + Wrapper2Z(T v) { value = v; } + } + static class Wrapper1Z { + @JsonFormat( + shape = JsonFormat.Shape.STRING, + pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS Z", + timezone = "Europe/Budapest" // +01:00 in winter + ) + public T value; + Wrapper1Z(T v) { value = v; } } + private final ObjectMapper MAPPER = mapperWithModuleBuilder() .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .build(); @Test - public void patternShouldNotEraseTimeZone() throws Exception + public void patternShouldNotEraseTimeZone() + throws Exception { // DateTime already in Europe/Budapest zone (no shift) _testSerialization( "{\"value\":\"2018-01-01T12:01:02.003 Europe/Budapest\"}", - new Wrapper<>(new DateTime(2018,1,1,12,1,2,3, - DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Budapest")))) - ); - + new Wrapper3Z<>(new DateTime(2018,1,1,12,1,2,3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Budapest"))))); // DateTime in UTC, should shift +1h _testSerialization( "{\"value\":\"2018-01-01T13:01:02.003 Europe/Budapest\"}", - new Wrapper<>(new DateTime(2018,1,1,12,1,2,3, - DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC")))) - ); - + new Wrapper3Z<>(new DateTime(2018,1,1,12,1,2,3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC"))))); // LocalDate _testSerialization( "{\"value\":\"2018-01-01T��:��:��.000 \"}", - new Wrapper<>(new LocalDate(2018,1,1)) - ); - + new Wrapper3Z<>(new LocalDate(2018,1,1))); // LocalTime _testSerialization( "{\"value\":\"����-��-��T12:01:02.003 \"}", - new Wrapper<>(new LocalTime(12,1,2,3)) - ); - + new Wrapper3Z<>(new LocalTime(12,1,2,3))); // LocalDateTime _testSerialization( "{\"value\":\"2018-01-01T12:01:02.003 \"}", - new Wrapper<>(new LocalDateTime(2018,1,1,12,1,2,3)) - ); + new Wrapper3Z<>(new LocalDateTime(2018,1,1,12,1,2,3))); + } + + @Test + public void patternShouldNotEraseTimeZoneWithZZ() + throws Exception + { + // DateTime already in Europe/Budapest zone (no shift) + _testSerialization( + "{\"value\":\"2018-01-01T12:01:02.003 +01:00\"}", + new Wrapper2Z<>(new DateTime(2018,1,1,12,1,2,3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Budapest"))))); + // DateTime in UTC, should shift +1h + _testSerialization( + "{\"value\":\"2018-01-01T13:01:02.003 +01:00\"}", + new Wrapper2Z<>(new DateTime(2018,1,1,12,1,2,3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC"))))); + } + + @Test + public void patternShouldNotEraseTimeZoneWithZ() + throws Exception + { + // DateTime already in Europe/Budapest zone (no shift) + _testSerialization( + "{\"value\":\"2018-01-01T12:01:02.003 +0100\"}", + new Wrapper1Z<>(new DateTime(2018,1,1,12,1,2,3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("Europe/Budapest"))))); + // DateTime in UTC, should shift +1h + _testSerialization( + "{\"value\":\"2018-01-01T13:01:02.003 +0100\"}", + new Wrapper1Z<>(new DateTime(2018,1,1,12,1,2,3, + DateTimeZone.forTimeZone(TimeZone.getTimeZone("UTC"))))); } private void _testSerialization(String expectedJson, Object wrapper) throws Exception { From 3d64bafbf14eb3a618e2642f0351eff0b94fca92 Mon Sep 17 00:00:00 2001 From: "Kim, Joo Hyuk" Date: Mon, 5 May 2025 02:17:58 +0900 Subject: [PATCH 7/7] Clean up behavior --- .../joda/cfg/JacksonJodaDateFormat.java | 12 ++--- .../datatype/joda/ser/DateTimeSerializer.java | 2 +- .../DateTimeOwnZoneSerialization92Test.java | 54 ++++++++----------- 3 files changed, 28 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java b/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java index e961c92..bbccb1a 100644 --- a/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java +++ b/src/main/java/com/fasterxml/jackson/datatype/joda/cfg/JacksonJodaDateFormat.java @@ -242,16 +242,12 @@ public DateTimeFormatter rawFormatter() { return _formatter; } + /** + * @deprecated since 2.19.1 Use {@link #createFormatter(SerializerProvider, DateTimeZone)} instead + */ public DateTimeFormatter createFormatter(SerializerProvider ctxt) { - DateTimeFormatter formatter = createFormatterWithLocale(ctxt); - if (!_explicitTimezone) { - TimeZone tz = ctxt.getTimeZone(); - if ((tz != null) && !tz.equals(_jdkTimezone)) { - formatter = formatter.withZone(DateTimeZone.forTimeZone(tz)); - } - } - return formatter; + return createFormatter(ctxt, null); } /** diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeSerializer.java index be2ffbf..788b35a 100644 --- a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeSerializer.java +++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeSerializer.java @@ -52,7 +52,7 @@ public void serialize(DateTime value, JsonGenerator gen, SerializerProvider prov if (numeric) { gen.writeNumber(value.getMillis()); } else { - gen.writeString(_format.createFormatter(provider).print(value)); + gen.writeString(_format.createFormatter(provider, value.getZone()).print(value)); } } else { // and then as per [datatype-joda#44], optional TimeZone inclusion diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeOwnZoneSerialization92Test.java b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeOwnZoneSerialization92Test.java index 25f4f3f..07621f2 100644 --- a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeOwnZoneSerialization92Test.java +++ b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeOwnZoneSerialization92Test.java @@ -1,48 +1,40 @@ package com.fasterxml.jackson.datatype.joda.ser; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.joda.JodaTestBase; +import java.util.TimeZone; + import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.junit.jupiter.api.Test; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.joda.JodaTestBase; + import static org.junit.jupiter.api.Assertions.assertEquals; -/** - * Reproduces jackson-datatype-joda#92: - * DateTime loses its own zone during serialization (normalised to UTC), - * so the JSON differs from Java 8 ZonedDateTime output. - * - * Expected (zone kept): "2017‑01‑01T01:01:01.000+08:00" - * Actual (current bug): "2016‑12‑31T17:01:01.000Z" - */ +// [dataformat-joda#92] DateTime serialization result is not same as Java 8 ZonedDateTime public class DateTimeOwnZoneSerialization92Test - extends JodaTestBase + extends JodaTestBase { - // Wrapper matches style used in other failing tests - static class Wrapper { - @JsonFormat(shape = JsonFormat.Shape.STRING, - pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS ZZZ") // default ISO pattern - public DateTime value; - Wrapper(DateTime v) { value = v; } - } - - private final ObjectMapper MAPPER = mapperWithModuleBuilder() - .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) // string mode - .build(); + private final ObjectMapper MAPPER = mapperWithModuleBuilder().disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).build(); @Test public void dateTimeShouldRetainItsOwnZone() throws Exception { - DateTime europe = new DateTime( - 2017, 1, 1, 12, 1, 1, 0, - DateTimeZone.forID("Europe/Budapest") // UTC+8 - ); + DateTime jodaZonedDateTime = new DateTime(2023, 10, 1, 12, 2, 3, 123, DateTimeZone.forID("Asia/Shanghai")); - String actual = MAPPER.writeValueAsString(new Wrapper(europe)); + // with WRITE_DATES_WITH_CONTEXT_TIME_ZONE + assertEquals("\"2023-10-01T12:02:03.123+08:00\"", + MAPPER.writer() + .with(TimeZone.getTimeZone("UTC")) + .with(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(jodaZonedDateTime)); - // This assertion FAILS today, demonstrating the bug - assertEquals("{\"value\":\"2017-01-01T11:01:01.000 Europe/Budapest\"}", actual); + // without WRITE_DATES_WITH_CONTEXT_TIME_ZONE + assertEquals("\"2023-10-01T12:02:03.123+08:00\"", + MAPPER.writer() + .with(TimeZone.getTimeZone("UTC")) + .without(SerializationFeature.WRITE_DATES_WITH_CONTEXT_TIME_ZONE) + .writeValueAsString(jodaZonedDateTime)); } + } \ No newline at end of file