Skip to content

Commit 0480206

Browse files
committed
Fix #66
1 parent f65d042 commit 0480206

File tree

5 files changed

+189
-4
lines changed

5 files changed

+189
-4
lines changed

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Joda (http://joda-time.sourceforge.net/) data types.
2323
</scm>
2424
<properties>
2525
<version.jackson.annotations>2.6.0-rc3</version.jackson.annotations>
26-
<version.jackson.core>2.6.0-rc3</version.jackson.core>
26+
<version.jackson.core>2.6.0-rc4-SNAPSHOT</version.jackson.core>
2727
<!-- Generate PackageVersion.java into this directory. -->
2828
<packageVersion.dir>com/fasterxml/jackson/datatype/joda</packageVersion.dir>
2929
<packageVersion.package>${project.groupId}.joda</packageVersion.package>

release-notes/VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Project: jackson-datatype-joda
1111
#62: Allow use of numbers-as-Strings for LocalDate (in array)
1212
(contributed by Michal Z)
1313
#64: Support `@JsonFormat(pattern=...)` for deserialization
14+
#66: Support `SerializationFeature.WRITE_DATES_WITH_ZONE_ID`
1415

1516
2.5.4 (not yet released)
1617

src/main/java/com/fasterxml/jackson/datatype/joda/deser/DateTimeDeserializer.java

+36
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,45 @@ public ReadableDateTime deserialize(JsonParser p, DeserializationContext ctxt)
5252
if (str.length() == 0) { // [JACKSON-360]
5353
return null;
5454
}
55+
// 08-Jul-2015, tatu: as per [datatype-joda#44], optional TimeZone inclusion
56+
// NOTE: on/off feature only for serialization; on deser should accept both
57+
int ix = str.indexOf('[');
58+
if (ix > 0) {
59+
int ix2 = str.lastIndexOf(']');
60+
String tzId = (ix2 < ix)
61+
? str.substring(ix+1)
62+
: str.substring(ix+1, ix2);
63+
DateTimeZone tz;
64+
try {
65+
tz = DateTimeZone.forID(tzId);
66+
} catch (IllegalArgumentException e) {
67+
throw ctxt.mappingException(String.format("Unknown DateTimeZone id '%s'", tzId));
68+
}
69+
str = str.substring(0, ix);
70+
71+
// One more thing; do we have plain timestamp?
72+
if (_allDigits(str)) {
73+
return new DateTime(Long.parseLong(str), tz);
74+
}
75+
return _format.createParser(ctxt)
76+
.parseDateTime(str)
77+
.withZone(tz);
78+
}
79+
5580
// Not sure if it should use timezone or not...
5681
return _format.createParser(ctxt).parseDateTime(str);
5782
}
5883
throw ctxt.mappingException(handledType());
5984
}
85+
86+
private static boolean _allDigits(String str)
87+
{
88+
for (int i = 0, len = str.length(); i < len; ++i) {
89+
int c = str.charAt(i);
90+
if (c > '9' | c < '0') {
91+
return false;
92+
}
93+
}
94+
return true;
95+
}
6096
}

src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeSerializer.java

+22-3
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,29 @@ public boolean isEmpty(SerializerProvider prov, DateTime value) {
3535
@Override
3636
public void serialize(DateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException
3737
{
38-
if (_useTimestamp(provider)) {
39-
gen.writeNumber(value.getMillis());
38+
// First: simple, non-timezone-included output
39+
if (!provider.isEnabled(SerializationFeature.WRITE_DATES_WITH_ZONE_ID)) {
40+
if (_useTimestamp(provider)) {
41+
gen.writeNumber(value.getMillis());
42+
} else {
43+
gen.writeString(_format.createFormatter(provider).print(value));
44+
}
4045
} else {
41-
gen.writeString(_format.createFormatter(provider).print(value));
46+
// and then as per [datatype-joda#44], optional TimeZone inclusion
47+
48+
StringBuilder sb;
49+
50+
if (_useTimestamp(provider)) {
51+
sb = new StringBuilder(20)
52+
.append(value.getMillis());
53+
} else {
54+
sb = new StringBuilder(40)
55+
.append(_format.createFormatter(provider).print(value));
56+
}
57+
sb = sb.append('[')
58+
.append(value.getZone())
59+
.append(']');
60+
gen.writeString(sb.toString());
4261
}
4362
}
4463
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package com.fasterxml.jackson.datatype.joda;
2+
3+
import org.joda.time.DateTime;
4+
import org.joda.time.DateTimeZone;
5+
6+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
7+
import com.fasterxml.jackson.databind.*;
8+
9+
// for [datatype-joda#44]
10+
public class TimeZoneTest extends JodaTestBase
11+
{
12+
// November 3, 2013 at 1:00a is the fall back DST transition that year in much of the US.
13+
private static final int FALL_BACK_YEAR = 2013;
14+
15+
private static final int FALL_BACK_MONTH = 11;
16+
17+
private static final int FALL_BACK_DAY = 3;
18+
19+
// The first one for America/Los_Angeles happens at 8:00 UTC.
20+
private static final int FIRST_FALL_BACK_HOUR = 8;
21+
22+
// And the second one happens at 9:00 UTC
23+
private static final int SECOND_FALL_BACK_HOUR = 9;
24+
25+
private static final DateTimeZone AMERICA_LOS_ANGELES = DateTimeZone.forID("America/Los_Angeles");
26+
27+
private final DateTime DATE_JAN_1_1970_UTC = new DateTime(0L, DateTimeZone.UTC);
28+
29+
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY, property = "@class")
30+
private static interface TypeInfoMixIn {
31+
}
32+
33+
/*
34+
/**********************************************************
35+
/* Test methods
36+
/**********************************************************
37+
*/
38+
39+
private final ObjectMapper MAPPER = jodaMapper();
40+
41+
public void testSimple() throws Exception
42+
{
43+
// First, no zone id included
44+
ObjectWriter w = MAPPER.writer()
45+
.without(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);
46+
47+
assertEquals("0",
48+
w.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
49+
.writeValueAsString(DATE_JAN_1_1970_UTC));
50+
assertEquals(quote("1970-01-01T00:00:00.000Z"),
51+
w.without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
52+
.writeValueAsString(DATE_JAN_1_1970_UTC));
53+
54+
// then with zone id
55+
56+
w = w.with(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);
57+
assertEquals(quote("0[UTC]"),
58+
w.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
59+
.writeValueAsString(DATE_JAN_1_1970_UTC));
60+
assertEquals(quote("1970-01-01T00:00:00.000Z[UTC]"),
61+
w.without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
62+
.writeValueAsString(DATE_JAN_1_1970_UTC));
63+
}
64+
65+
public void testRoundTrip() throws Exception
66+
{
67+
ObjectWriter w = MAPPER.writer()
68+
.with(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);
69+
DateTime input = new DateTime(2014, 8, 24, 5, 17, 45, DateTimeZone.forID("America/Chicago")); // arbitrary
70+
71+
// First as timestamp
72+
73+
String json = w.with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
74+
.writeValueAsString(input);
75+
DateTime result = MAPPER.readValue(json, DateTime.class);
76+
assertEquals("Actual timepoints differ", input.getMillis(), result.getMillis());
77+
assertEquals("TimeZones differ", input, result);
78+
79+
// then as regular tet
80+
json = w.without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
81+
.writeValueAsString(input);
82+
result = MAPPER.readValue(json, DateTime.class);
83+
assertEquals("Actual timepoints differ", input.getMillis(), result.getMillis());
84+
assertEquals("TimeZones differ", input, result);
85+
}
86+
87+
/**
88+
* Test that de/serializing an ambiguous time (e.g. a 'fall back' DST transition) works and preserves the proper
89+
* instants in time and time zones.
90+
*/
91+
public void testFallBackTransition() throws Exception
92+
{
93+
DateTime firstOneAmUtc = new DateTime(FALL_BACK_YEAR, FALL_BACK_MONTH, FALL_BACK_DAY, FIRST_FALL_BACK_HOUR, 0, 0,
94+
DateTimeZone.UTC);
95+
DateTime secondOneAmUtc = new DateTime(FALL_BACK_YEAR, FALL_BACK_MONTH, FALL_BACK_DAY, SECOND_FALL_BACK_HOUR, 0, 0,
96+
DateTimeZone.UTC);
97+
98+
DateTime firstOneAm = new DateTime(firstOneAmUtc, AMERICA_LOS_ANGELES);
99+
DateTime secondOneAm = new DateTime(secondOneAmUtc, AMERICA_LOS_ANGELES);
100+
101+
ObjectWriter w = MAPPER.writer()
102+
.without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
103+
.with(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);
104+
105+
String firstOneAmStr = w.writeValueAsString(firstOneAm);
106+
String secondOneAmStr = w.writeValueAsString(secondOneAm);
107+
108+
DateTime firstRoundTrip = MAPPER.readValue(firstOneAmStr, DateTime.class);
109+
DateTime secondRoundTrip = MAPPER.readValue(secondOneAmStr, DateTime.class);
110+
111+
assertEquals("Actual timepoints differ", firstOneAm.getMillis(), firstRoundTrip.getMillis());
112+
assertEquals("TimeZones differ", firstOneAm, firstRoundTrip);
113+
114+
assertEquals("Actual timepoints differ", secondOneAm.getMillis(), secondRoundTrip.getMillis());
115+
assertEquals("TimeZones differ", secondOneAm, secondRoundTrip);
116+
}
117+
118+
public void testSerializationWithTypeInfo() throws Exception
119+
{
120+
// but if re-configured to include the time zone
121+
ObjectMapper m = jodaMapper();
122+
m.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
123+
m.enable(SerializationFeature.WRITE_DATES_WITH_ZONE_ID);
124+
125+
m.addMixIn(DateTime.class, TypeInfoMixIn.class);
126+
assertEquals("[\"org.joda.time.DateTime\",\"0[UTC]\"]",
127+
m.writeValueAsString(DATE_JAN_1_1970_UTC));
128+
}
129+
}

0 commit comments

Comments
 (0)