Skip to content

Commit

Permalink
change DateTime de/serialization to preserve time zone
Browse files Browse the repository at this point in the history
  • Loading branch information
David Byron committed Sep 1, 2014
1 parent 531edb3 commit 40a9365
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,20 @@ public ReadableDateTime deserialize(JsonParser jp, DeserializationContext ctxt)
if (str.length() == 0) { // [JACKSON-360]
return null;
}
if (ctxt.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE))
return new DateTime(str, dtz);
else
return DateTime.parse(str);
// Split the string at the first slash. If there's no first
// slash, assume we're dealing with an ISO8601 serialization.
int firstSlash = str.indexOf('/');
if (firstSlash == -1) {
if (ctxt.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE))
return new DateTime(str, dtz);
else
return DateTime.parse(str);
}

String millisStr = str.substring(0, firstSlash);
String zoneStr = str.substring(firstSlash + 1);

return new DateTime(Long.valueOf(millisStr), DateTimeZone.forID(zoneStr));
}
// TODO: in 2.4, use 'handledType()'
throw ctxt.mappingException(getValueClass());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,39 @@ public void serialize(DateTime value, JsonGenerator jgen, SerializerProvider pro
if (_useTimestamp(provider)) {
jgen.writeNumber(value.getMillis());
} else {
jgen.writeString(_format.createFormatter(provider).print(value));
// Unfortunately, I don't see a way to include milliseconds since
// the epoch in a DateTimeFormatter or this code (and the
// deserializer) could likely be simpler...Perhaps like this:
//
// // The provider has a time zone, but let's not use it. Use the time
// // zone from value instead since the goal is to preserve that across de/serialization.
// DateTimeFormatter providerFormatter = _format.createFormatter(provider);
// DateTimeFormatter valueFormatter = providerFormatter.withZone(value.getZone());
// jgen.writeString(valueFormatter.print(value));
//
// Instead, if someone has set an explicit format, use it.
if (_format != DEFAULT_FORMAT) {
jgen.writeString(_format.createFormatter(provider).print(value));
} else {
// Store value's time zone explicitly in addition to some
// representation of the time so it survives a de/serialization
// round trip. Assume that this is the desired behavior for all
// DateTime instances. If someone only cares about the instant
// in time, suggest s/he use Instant.
StringBuilder timeWithZone = new StringBuilder();

// Store the time in UTC to avoid the ambiguity that occurs on a
// "fall back" DST transition. For example, on November 3, 2013
// in America/Los_Angeles, 1:00a happens twice...once at 8:00:00
// UTC (before falling back) and once at 9:00:00 UTC (after
// falling back). From there we have a choice between an
// ISO8601 string or milliseconds since the epoch. I'm going
// with milliseconds since the epoch since it's more compact.
timeWithZone.append(value.getMillis());
timeWithZone.append('/');
timeWithZone.append(value.getZone().toString());
jgen.writeString(timeWithZone.toString());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public void testSerializationFeatureNoTimestamp() throws IOException
{
ObjectMapper m = jodaMapper();
m.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
assertEquals(quote("1970-01-01T00:00:00.000Z"), m.writeValueAsString(DATE_JAN_1_1970_UTC));
assertEquals(quote("0/UTC"), m.writeValueAsString(DATE_JAN_1_1970_UTC));
}

/**
Expand Down Expand Up @@ -151,11 +151,11 @@ public void testSerializationWithTypeInfo() throws IOException
// by default, dates use timestamp, so:
assertEquals("0", MAPPER.writeValueAsString(dt));

// but if re-configured, as regular ISO-8601 string
// but if re-configured to include the time zone
ObjectMapper m = jodaMapper();
m.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
m.addMixInAnnotations(DateTime.class, ObjectConfiguration.class);
assertEquals("[\"org.joda.time.DateTime\",\"1970-01-01T00:00:00.000Z\"]",
assertEquals("[\"org.joda.time.DateTime\",\"0/UTC\"]",
m.writeValueAsString(dt));
}
}

0 comments on commit 40a9365

Please sign in to comment.