diff --git a/release-notes/VERSION b/release-notes/VERSION
index fee9799a..8b1962e3 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -6,6 +6,8 @@ Project: jackson-datatype-joda
2.8.0 (not yet released)
+#83: WRITE_DATES_WITH_ZONE_ID feature not working when applied on @JsonFormat annotation
+ (reported by mandyWW@github)
#87: Add support for JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE
(contributed by Alexey B)
- Support "config overrides" for `@JsonFormat.Value` added in databind
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 215b1fd4..802e3316 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
@@ -7,8 +7,10 @@
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
+import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
/**
@@ -27,8 +29,20 @@ public class JacksonJodaDateFormat extends JacksonJodaFormatBase
protected final boolean _explicitTimezone;
+ /**
+ * Flag for JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE
+ *
+ * @since 2.8
+ */
protected final Boolean _adjustToContextTZOverride;
+ /**
+ * Flag for JsonFormat.Feature.WRITE_DATES_WITH_ZONE_ID
+ *
+ * @since 2.8
+ */
+ protected final Boolean _writeZoneId;
+
public JacksonJodaDateFormat(DateTimeFormatter defaultFormatter)
{
super();
@@ -37,16 +51,29 @@ public JacksonJodaDateFormat(DateTimeFormatter defaultFormatter)
_jdkTimezone = (tz == null) ? null : tz.toTimeZone();
_explicitTimezone = false;
_adjustToContextTZOverride = null;
+ _writeZoneId = null;
}
public JacksonJodaDateFormat(JacksonJodaDateFormat base,
- Boolean useTimestamp, Boolean adjustToContextTZOverride)
+ Boolean useTimestamp)
{
super(base, useTimestamp);
_formatter = base._formatter;
_jdkTimezone = base._jdkTimezone;
_explicitTimezone = base._explicitTimezone;
+ _adjustToContextTZOverride = base._adjustToContextTZOverride;
+ _writeZoneId = base._writeZoneId;
+ }
+
+ public JacksonJodaDateFormat(JacksonJodaDateFormat base,
+ Boolean adjustToContextTZOverride, Boolean writeZoneId)
+ {
+ super(base);
+ _formatter = base._formatter;
+ _jdkTimezone = base._jdkTimezone;
+ _explicitTimezone = base._explicitTimezone;
_adjustToContextTZOverride = adjustToContextTZOverride;
+ _writeZoneId = writeZoneId;
}
public JacksonJodaDateFormat(JacksonJodaDateFormat base,
@@ -57,6 +84,7 @@ public JacksonJodaDateFormat(JacksonJodaDateFormat base,
_jdkTimezone = base._jdkTimezone;
_explicitTimezone = base._explicitTimezone;
_adjustToContextTZOverride = base._adjustToContextTZOverride;
+ _writeZoneId = base._writeZoneId;
}
public JacksonJodaDateFormat(JacksonJodaDateFormat base, TimeZone jdkTimezone)
@@ -66,6 +94,7 @@ public JacksonJodaDateFormat(JacksonJodaDateFormat base, TimeZone jdkTimezone)
_jdkTimezone = jdkTimezone;
_explicitTimezone = true;
_adjustToContextTZOverride = base._adjustToContextTZOverride;
+ _writeZoneId = base._writeZoneId;
}
public JacksonJodaDateFormat(JacksonJodaDateFormat base, Locale locale)
@@ -75,6 +104,7 @@ public JacksonJodaDateFormat(JacksonJodaDateFormat base, Locale locale)
_jdkTimezone = base._jdkTimezone;
_explicitTimezone = base._explicitTimezone;
_adjustToContextTZOverride = base._adjustToContextTZOverride;
+ _writeZoneId = base._writeZoneId;
}
/*
@@ -83,11 +113,25 @@ public JacksonJodaDateFormat(JacksonJodaDateFormat base, Locale locale)
/**********************************************************
*/
+ public JacksonJodaDateFormat with(JsonFormat.Value ann) {
+ JacksonJodaDateFormat format = this;
+ format = format.withLocale(ann.getLocale());
+ format = format.withTimeZone(ann.getTimeZone());
+ format = format.withFormat(ann.getPattern().trim());
+ Boolean adjustTZ = ann.getFeature(JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
+ Boolean writeZoneId = ann.getFeature(JsonFormat.Feature.WRITE_DATES_WITH_ZONE_ID);
+ if ((adjustTZ != _adjustToContextTZOverride)
+ || (writeZoneId != _writeZoneId)) {
+ format = new JacksonJodaDateFormat(format, adjustTZ, writeZoneId);
+ }
+ return format;
+ }
+
public JacksonJodaDateFormat withUseTimestamp(Boolean useTimestamp) {
if ((_useTimestamp != null) && _useTimestamp.equals(useTimestamp)) {
return this;
}
- return new JacksonJodaDateFormat(this, useTimestamp, _adjustToContextTZOverride);
+ return new JacksonJodaDateFormat(this, useTimestamp);
}
public JacksonJodaDateFormat withFormat(String format) {
@@ -121,12 +165,26 @@ public JacksonJodaDateFormat withLocale(Locale locale) {
return new JacksonJodaDateFormat(this, locale);
}
+ /**
+ * @since 2.8
+ */
public JacksonJodaDateFormat withAdjustToContextTZOverride(Boolean adjustToContextTZOverride) {
// minor efficiency check to avoid recreation if no change:
if (adjustToContextTZOverride == _adjustToContextTZOverride) {
return this;
}
- return new JacksonJodaDateFormat(this, _useTimestamp, adjustToContextTZOverride);
+ return new JacksonJodaDateFormat(this, adjustToContextTZOverride, _writeZoneId);
+ }
+
+ /**
+ * @since 2.8
+ */
+ public JacksonJodaDateFormat withWriteZoneId(Boolean writeZoneId) {
+ // minor efficiency check to avoid recreation if no change:
+ if (writeZoneId == _writeZoneId) {
+ return this;
+ }
+ return new JacksonJodaDateFormat(this, _adjustToContextTZOverride, writeZoneId);
}
/*
@@ -201,7 +259,7 @@ public DateTimeFormatter createParser(DeserializationContext ctxt)
}
}
if (!_explicitTimezone) {
- if (isAdjustDatesToContextTimeZone(ctxt)) {
+ if (shouldAdjustToContextTimeZone(ctxt)) {
TimeZone tz = ctxt.getTimeZone();
if (tz != null && !tz.equals(_jdkTimezone)) {
formatter = formatter.withZone(DateTimeZone.forTimeZone(tz));
@@ -213,9 +271,20 @@ public DateTimeFormatter createParser(DeserializationContext ctxt)
return formatter;
}
- private boolean isAdjustDatesToContextTimeZone(DeserializationContext ctxt) {
- return (_adjustToContextTZOverride != null) ? _adjustToContextTZOverride :
- ctxt.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
+ /**
+ * @since 2.8
+ */
+ public boolean shouldAdjustToContextTimeZone(DeserializationContext ctxt) {
+ return (_adjustToContextTZOverride != null) ? _adjustToContextTZOverride :
+ ctxt.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
+ }
+
+ /**
+ * @since 2.8
+ */
+ public boolean shouldWriteWithZoneId(SerializerProvider ctxt) {
+ return (_writeZoneId != null) ? _writeZoneId :
+ ctxt.isEnabled(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);
}
/**
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/JodaDateDeserializerBase.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/JodaDateDeserializerBase.java
index df76b144..e6003310 100644
--- a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/JodaDateDeserializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/JodaDateDeserializerBase.java
@@ -53,11 +53,7 @@ public JsonDeserializer> createContextual(DeserializationContext ctxt,
format = format.withUseTimestamp(useTimestamp);
}
// for others, safe to call, null/empty just ignored
- format = format.withFormat(ann.getPattern().trim());
- format = format.withLocale(ann.getLocale());
- format = format.withTimeZone(ann.getTimeZone());
- format = format.withAdjustToContextTZOverride(
- ann.getFeature(JsonFormat.Feature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE));
+ format = format.with(ann);
if (format != _format) {
return withFormat(format);
}
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 e456cd4f..5c9868f2 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
@@ -36,7 +36,7 @@ public boolean isEmpty(SerializerProvider prov, DateTime value) {
public void serialize(DateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException
{
// First: simple, non-timezone-included output
- if (!provider.isEnabled(SerializationFeature.WRITE_DATES_WITH_ZONE_ID)) {
+ if (!writeWithZoneId(provider)) {
if (_useTimestamp(provider)) {
gen.writeNumber(value.getMillis());
} else {
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JodaDateSerializerBase.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JodaDateSerializerBase.java
index 63013492..4a92eb43 100644
--- a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JodaDateSerializerBase.java
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JodaDateSerializerBase.java
@@ -67,10 +67,7 @@ public JsonSerializer> createContextual(SerializerProvider prov,
if (useTimestamp != null) {
format = format.withUseTimestamp(useTimestamp);
}
- // for others, safe to call, null/empty just ignored
- format = format.withFormat(ann.getPattern().trim());
- format = format.withLocale(ann.getLocale());
- format = format.withTimeZone(ann.getTimeZone());
+ format = format.with(ann);
if (format != _format) {
return withFormat(format);
}
@@ -125,4 +122,11 @@ protected void _acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaTy
protected boolean _useTimestamp(SerializerProvider provider) {
return _format.useTimestamp(provider, _featureForNumeric);
}
+
+ /**
+ * @since 2.8
+ */
+ protected boolean writeWithZoneId(SerializerProvider provider) {
+ return _format.shouldWriteWithZoneId(provider);
+ }
}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/JodaSerializationTest.java b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JodaSerializationTest.java
similarity index 99%
rename from src/test/java/com/fasterxml/jackson/datatype/joda/JodaSerializationTest.java
rename to src/test/java/com/fasterxml/jackson/datatype/joda/ser/JodaSerializationTest.java
index 8bf6708c..443a222b 100644
--- a/src/test/java/com/fasterxml/jackson/datatype/joda/JodaSerializationTest.java
+++ b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/JodaSerializationTest.java
@@ -1,4 +1,4 @@
-package com.fasterxml.jackson.datatype.joda;
+package com.fasterxml.jackson.datatype.joda.ser;
import java.io.IOException;
import java.text.SimpleDateFormat;
@@ -11,6 +11,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.joda.JodaTestBase;
public class JodaSerializationTest extends JodaTestBase
{
diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/ser/WriteZoneIdTest.java b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/WriteZoneIdTest.java
new file mode 100644
index 00000000..1b73c2a1
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/joda/ser/WriteZoneIdTest.java
@@ -0,0 +1,43 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.joda.JodaTestBase;
+
+public class WriteZoneIdTest extends JodaTestBase
+{
+ static class DummyClassWithDate {
+ @JsonFormat(shape = JsonFormat.Shape.STRING,
+ pattern = "dd-MM-yyyy hh:mm:ss Z",
+ with = JsonFormat.Feature.WRITE_DATES_WITH_ZONE_ID)
+ public DateTime date;
+
+ DummyClassWithDate() { }
+
+ public DummyClassWithDate(DateTime date) {
+ this.date = date;
+ }
+ }
+
+ public void testJacksonAnnotatedPOJOWithDateWithTimezoneToJson() throws Exception
+ {
+ ObjectMapper mapper = jodaMapper();
+ String ZONE_ID = "Asia/Krasnoyarsk";
+
+ DummyClassWithDate input = new DummyClassWithDate(new DateTime(2015, 11, 23, 22, 06, 39,
+ DateTimeZone.forID(ZONE_ID)));
+ // 30-Jun-2016, tatu: Exact time seems to vary a bit based on DST, so let's actually
+ // just verify appending of timezone id itself:
+ String json = mapper.writeValueAsString(input);
+ if (!json.contains("\"23-11-2015")) {
+ fail("Should contain time prefix, did not: "+json);
+ }
+ String match = String.format("[%s]", ZONE_ID);
+ if (!json.contains(match)) {
+ fail("Should contain zone id "+match+", does not: "+json);
+ }
+ }
+}