A java.util.Date
doesn't have any timezone information. Once you deserialize a String
to a Date
, the offset +0300
is lost: the date keeps just the timestamp value, and it can't know what's the original timezone it came from.
If the output must always be in +03:00
offset, you can set it directly in the respective fields, using the com.fasterxml.jackson.annotation.JsonFormat
annotation:
@JsonFormat(timezone = "GMT+03:00")
private Date startDateTime;
@JsonFormat(timezone = "GMT+03:00")
private Date endDateTime;
With this, the date fields will always be serialized to +03:00
offset:
{
"startDateTime":"2017-10-09T22:43:07.109+0300",
"endDateTime":"2017-10-09T21:40:07.109+0300"
}
If the inputs can be in any other offset (not only +03:00
) and you want to preserve it, the java.util.Date
isn't the ideal type. One alternative is to use Jackson Modules Java 8, if you're using Java >= 8.
For Java 6 and 7, there's the ThreeTen Backport and the corresponding Jackson module - I haven't tested, though, but the code might be similar, as the ThreeTen Backport contains the same classes and methods, only the package is different - (in Java 8 is java.time
and in ThreeTen Backport is org.threeten.bp
).
To preserve the date, time and offset, the best alternative is the OffsetDateTime
class. So you just need to change the fields type and set the corresponding format to it:
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXX")
private OffsetDateTime startDateTime;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXX")
private OffsetDateTime endDateTime;
In the object mapper, you must also register the JavaTimeModule
and disable the ADJUST_DATES_TO_CONTEXT_TIME_ZONE
feature, so the offsets are preserved (the default behaviour is to convert to Jackson context's timezone, which might not be the same used in the inputs - by disabling this, the offset is preserved).
You can use a JacksonConfigurator
(as explained in this answer) and do these configurations:
ObjectMapper om = new ObjectMapper();
om.registerModule(new JavaTimeModule());
om.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
This configuration is usually enough, but you can also set SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
to false
as well, just in case.
If you still need to work with java.util.Date
, you can use the API to convert from/to it. In Java 8, there's the new Date.from
method:
// convert to java.util.Date
public Date getStartAsJavaUtilDate() {
return Date.from(startDateTime.toInstant());
}
And in ThreeTen Backport, there's the org.threeten.bp.DateTimeUtils
class:
// convert to java.util.Date
DateTimeUtils.toDate(startDateTime.toInstant());
To convert a Date
back to OffsetDateTime
, though, it's more tricky. The Date
object has no timezone information, so it can't know the original offset. One alternative is to keep the original offset in a separate variable:
// keep the original offset
ZoneOffset startDateOffset = startDateTime.getOffset();
Then, you can convert the Date
to Instant
, and then convert it to the original offset:
// convert java.util.Date to original offset (Java 8)
startDateTime = date.toInstant().atOffset(startDateOffset);
// ThreeTen Backport
startDateTime = DateTimeUtils.toInstant(date).atOffset(startDateOffset);