Skip to content

Commit 91a3636

Browse files
committed
Fix #1762 (for 3.0)
1 parent d402a0f commit 91a3636

File tree

6 files changed

+85
-149
lines changed

6 files changed

+85
-149
lines changed

release-notes/VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ Versions: 3.x (for earlier see VERSION-2.x)
77

88
3.0.0 (not yet released)
99

10-
10+
#1762: `StdDateFormat`: serialize time offset using colon
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,74 @@
11
package com.fasterxml.jackson.databind.ext;
22

3+
import java.beans.ConstructorProperties;
4+
import java.beans.Transient;
5+
import java.nio.file.Path;
6+
37
import com.fasterxml.jackson.databind.JsonDeserializer;
48
import com.fasterxml.jackson.databind.JsonSerializer;
59
import com.fasterxml.jackson.databind.PropertyName;
610
import com.fasterxml.jackson.databind.introspect.Annotated;
711
import com.fasterxml.jackson.databind.introspect.AnnotatedParameter;
8-
import com.fasterxml.jackson.databind.util.ClassUtil;
12+
import com.fasterxml.jackson.databind.introspect.AnnotatedWithParams;
913

1014
/**
11-
* To support Java7-incomplete platforms, we will offer support for JDK 7
12-
* annotations through this class, loaded dynamically; if loading fails,
13-
* support will be missing. This class is the non-JDK-7-dependent API,
14-
* and {@link Java7SupportImpl} is JDK7-dependent implementation of
15-
* functionality.
15+
* Due to historical reasons, Java 7 type support has been separate.
1616
*/
17-
public abstract class Java7Support
17+
public class Java7Support
1818
{
19-
private final static Java7Support IMPL;
20-
21-
static {
22-
Java7Support impl = null;
23-
try {
24-
Class<?> cls = Class.forName("com.fasterxml.jackson.databind.ext.Java7SupportImpl");
25-
impl = (Java7Support) ClassUtil.createInstance(cls, false);
26-
} catch (Throwable t) {
27-
// 24-Nov-2015, tatu: Should we log or not?
28-
java.util.logging.Logger.getLogger(Java7Support.class.getName())
29-
.warning("Unable to load JDK7 types (annotations, java.nio.file.Path): no Java7 support added");
30-
}
31-
IMPL = impl;
32-
}
33-
3419
public static Java7Support instance() {
35-
return IMPL;
20+
return new Java7Support();
3621
}
37-
38-
public abstract Boolean findTransient(Annotated a);
3922

40-
public abstract Boolean hasCreatorAnnotation(Annotated a);
23+
public Class<?> getClassJavaNioFilePath() {
24+
return Path.class;
25+
}
4126

42-
public abstract PropertyName findConstructorName(AnnotatedParameter p);
27+
public JsonDeserializer<?> getDeserializerForJavaNioFilePath(Class<?> rawType) {
28+
if (rawType == Path.class) {
29+
return new NioPathDeserializer();
30+
}
31+
return null;
32+
}
4333

44-
public abstract Class<?> getClassJavaNioFilePath();
34+
public JsonSerializer<?> getSerializerForJavaNioFilePath(Class<?> rawType) {
35+
if (Path.class.isAssignableFrom(rawType)) {
36+
return new NioPathSerializer();
37+
}
38+
return null;
39+
}
4540

46-
public abstract JsonDeserializer<?> getDeserializerForJavaNioFilePath(Class<?> rawType);
41+
public Boolean findTransient(Annotated a) {
42+
Transient t = a.getAnnotation(Transient.class);
43+
if (t != null) {
44+
return t.value();
45+
}
46+
return null;
47+
}
48+
49+
public Boolean hasCreatorAnnotation(Annotated a) {
50+
ConstructorProperties props = a.getAnnotation(ConstructorProperties.class);
51+
// 08-Nov-2015, tatu: One possible check would be to ensure there is at least
52+
// one name iff constructor has arguments. But seems unnecessary for now.
53+
if (props != null) {
54+
return Boolean.TRUE;
55+
}
56+
return null;
57+
}
4758

48-
public abstract JsonSerializer<?> getSerializerForJavaNioFilePath(Class<?> rawType);
49-
}
59+
public PropertyName findConstructorName(AnnotatedParameter p)
60+
{
61+
AnnotatedWithParams ctor = p.getOwner();
62+
if (ctor != null) {
63+
ConstructorProperties props = ctor.getAnnotation(ConstructorProperties.class);
64+
if (props != null) {
65+
String[] names = props.value();
66+
int ix = p.getIndex();
67+
if (ix < names.length) {
68+
return PropertyName.construct(names[ix]);
69+
}
70+
}
71+
}
72+
return null;
73+
}
74+
}

src/main/java/com/fasterxml/jackson/databind/ext/Java7SupportImpl.java

-86
This file was deleted.

src/main/java/com/fasterxml/jackson/databind/util/StdDateFormat.java

+10-13
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ public class StdDateFormat
129129
*<p>
130130
* Cannot be `final` because {@link #setLenient(boolean)} returns
131131
* `void`.
132-
*
133-
* @since 2.7
134132
*/
135133
protected Boolean _lenient;
136134

@@ -145,10 +143,10 @@ public class StdDateFormat
145143

146144
/**
147145
* Whether the TZ offset must be formatted with a colon between hours and minutes ({@code HH:mm} format)
148-
*
149-
* @since 2.9.1
146+
*<p>
147+
* NOTE: default changed to `true` in Jackson 3.0; was `false` earlier.
150148
*/
151-
private boolean _tzSerializedWithColon = false;
149+
protected boolean _tzSerializedWithColon = true;
152150

153151
/*
154152
/**********************************************************
@@ -428,12 +426,11 @@ protected void _format(TimeZone tz, Locale loc, Date date,
428426
// 24-Jun-2017, tatu: While `Z` would be conveniently short, older specs
429427
// mandate use of full `+0000`
430428
// formatted.append('Z');
431-
if( _tzSerializedWithColon ) {
432-
buffer.append("+00:00");
433-
}
434-
else {
435-
buffer.append("+0000");
436-
}
429+
if (_tzSerializedWithColon ) {
430+
buffer.append("+00:00");
431+
} else {
432+
buffer.append("+0000");
433+
}
437434
}
438435
}
439436

@@ -496,12 +493,12 @@ public String toPattern() { // same as SimpleDateFormat
496493
return sb.toString();
497494
}
498495

499-
@Override // since 2.7[.2], as per [databind#1130]
496+
@Override
500497
public boolean equals(Object o) {
501498
return (o == this);
502499
}
503500

504-
@Override // since 2.7[.2], as per [databind#1130]
501+
@Override
505502
public int hashCode() {
506503
return System.identityHashCode(this);
507504
}

src/test/java/com/fasterxml/jackson/databind/ext/TestCoreXMLTypes.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ private String removeZ(String dateStr) {
7171
if (dateStr.endsWith("Z")) {
7272
return dateStr.substring(0, dateStr.length()-1);
7373
}
74-
if (dateStr.endsWith("+0000")) {
75-
return dateStr.substring(0, dateStr.length()-5);
74+
if (dateStr.endsWith("+00:00")) {
75+
return dateStr.substring(0, dateStr.length()-6);
7676
}
7777
return dateStr;
7878
}

src/test/java/com/fasterxml/jackson/databind/ser/jdk/DateSerializationTest.java

+16-16
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ public void testDateISO8601() throws IOException
117117
ObjectMapper mapper = new ObjectMapper();
118118
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
119119

120-
serialize( mapper, judate(1970, 1, 1, 02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+0000");
121-
serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T00:00:00.000+0000");
120+
serialize( mapper, judate(1970, 1, 1, 02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+00:00");
121+
serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T00:00:00.000+00:00");
122122
}
123123

124124
/**
@@ -130,8 +130,8 @@ public void testDateISO8601_customTZ() throws IOException
130130
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
131131
mapper.setTimeZone(TimeZone.getTimeZone("GMT+2"));
132132

133-
serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+0200");
134-
serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T02:00:00.000+0200");
133+
serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+02:00");
134+
serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T02:00:00.000+02:00");
135135
}
136136

137137
/**
@@ -142,16 +142,16 @@ public void testDateISO8601_customTZ() throws IOException
142142
public void testDateISO8601_colonInTZ() throws IOException
143143
{
144144
StdDateFormat dateFormat = new StdDateFormat();
145-
assertFalse(dateFormat.isColonIncludedInTimeZone());
146-
dateFormat = dateFormat.withColonInTimeZone(true);
147145
assertTrue(dateFormat.isColonIncludedInTimeZone());
146+
dateFormat = dateFormat.withColonInTimeZone(false);
147+
assertFalse(dateFormat.isColonIncludedInTimeZone());
148148

149149
ObjectMapper mapper = new ObjectMapper();
150150
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
151151
mapper.setDateFormat(dateFormat);
152152

153-
serialize( mapper, judate(1970, 1, 1, 02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+00:00");
154-
serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T00:00:00.000+00:00");
153+
serialize( mapper, judate(1970, 1, 1, 02, 00, 00, 0, "GMT+2"), "1970-01-01T00:00:00.000+0000");
154+
serialize( mapper, judate(1970, 1, 1, 00, 00, 00, 0, "UTC"), "1970-01-01T00:00:00.000+0000");
155155
}
156156

157157
public void testDateOther() throws IOException
@@ -202,7 +202,7 @@ public void testDatesAsMapKeys() throws IOException
202202
assertFalse(mapper.isEnabled(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS));
203203
map.put(new Date(0L), Integer.valueOf(1));
204204
// by default will serialize as ISO-8601 values...
205-
assertEquals("{\"1970-01-01T00:00:00.000+0000\":1}", mapper.writeValueAsString(map));
205+
assertEquals("{\"1970-01-01T00:00:00.000+00:00\":1}", mapper.writeValueAsString(map));
206206

207207
// but can change to use timestamps too
208208
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, true);
@@ -234,7 +234,7 @@ public void testDateWithJsonFormat() throws Exception
234234

235235
// and with default (ISO8601) format (databind#1109)
236236
json = mapper.writeValueAsString(new DateAsDefaultStringBean(0L));
237-
assertEquals("{\"date\":\"1970-01-01T00:00:00.000+0000\"}", json);
237+
assertEquals("{\"date\":\"1970-01-01T00:00:00.000+00:00\"}", json);
238238
}
239239

240240
/**
@@ -275,15 +275,15 @@ public void testDateDefaultShape() throws Exception
275275
assertEquals(aposToQuotes("{'date':0}"), json);
276276
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
277277
json = mapper.writeValueAsString(new DateAsDefaultBean(0L));
278-
assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+0000'}"), json);
278+
assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+00:00'}"), json);
279279

280280
// Empty @JsonFormat => default to user config
281281
mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
282282
json = mapper.writeValueAsString(new DateAsDefaultBeanWithEmptyJsonFormat(0L));
283283
assertEquals(aposToQuotes("{'date':0}"), json);
284284
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
285285
json = mapper.writeValueAsString(new DateAsDefaultBeanWithEmptyJsonFormat(0L));
286-
assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+0000'}"), json);
286+
assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+00:00'}"), json);
287287

288288
// @JsonFormat with Shape.ANY and pattern => STRING shape, regardless of user config
289289
mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
@@ -296,18 +296,18 @@ public void testDateDefaultShape() throws Exception
296296
// @JsonFormat with Shape.ANY and locale => STRING shape, regardless of user config
297297
mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
298298
json = mapper.writeValueAsString(new DateAsDefaultBeanWithLocale(0L));
299-
assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+0000'}"), json);
299+
assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+00:00'}"), json);
300300
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
301301
json = mapper.writeValueAsString(new DateAsDefaultBeanWithLocale(0L));
302-
assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+0000'}"), json);
302+
assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000+00:00'}"), json);
303303

304304
// @JsonFormat with Shape.ANY and timezone => STRING shape, regardless of user config
305305
mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
306306
json = mapper.writeValueAsString(new DateAsDefaultBeanWithTimezone(0L));
307-
assertEquals(aposToQuotes("{'date':'1970-01-01T01:00:00.000+0100'}"), json);
307+
assertEquals(aposToQuotes("{'date':'1970-01-01T01:00:00.000+01:00'}"), json);
308308
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
309309
json = mapper.writeValueAsString(new DateAsDefaultBeanWithTimezone(0L));
310-
assertEquals(aposToQuotes("{'date':'1970-01-01T01:00:00.000+0100'}"), json);
310+
assertEquals(aposToQuotes("{'date':'1970-01-01T01:00:00.000+01:00'}"), json);
311311
}
312312

313313
// [databind#1648]: contextual default format should be used

0 commit comments

Comments
 (0)