使用Jackson(Joda-Time模块)进行DateTime反序列化的默认时区

时间:2013-11-26 16:18:29

标签: java datetime jackson deserialization jodatime

此问题是针对杰克逊使用Joda-Time DateTime jackson-datatype-joda module 进行反序列化。是否有一个默认时区,日期字符串将被反序列化?如果是这样,它是什么?是 UTC 吗?

我需要问这个,因为杰克逊的文档并不是专门针对Joda-Time DateTime的。我在本文(http://wiki.fasterxml.com/JacksonFAQDateHandling)中发现,杰克逊将 GMT 作为反序列化为java.util.Datejava.util.Calendar的默认时区。但是,本文档中没有提及Joda-Time数据类型。另外,我特别需要字符串使用 UTC 时区反序列化为DateTime个对象,而不是GMT:尽管这两个区域非常相似,但是存在一些小差异,因此GMT不会对我来说是可行的。

谢谢。

4 个答案:

答案 0 :(得分:22)

DateTimeDeserializer的源代码显示它使用DeserializationContext的时区,该时区由反序列化期间的ObjectMapper提供。如果您查看ObjectMapper API,您会看到有设置时区的方法:

public ObjectMapper setTimeZone(TimeZone tz)

因此,您可以使用此方法配置ObjectMapper并将时区设置为正确的时区。

对于默认值的问题,似乎Javadoc说了一件事,但代码显示了另一件事。

ObjectMapper.setTimeZone(TimeZone tz)的Javadoc:

/**
  * Method for overriding default TimeZone to use for formatting.
  * Default value used is {@link TimeZone#getDefault()}.
  */

但是,代码明确设置了时区:

protected final static BaseSettings DEFAULT_BASE = new BaseSettings(
    ...
    // TimeZone.getDefault()
    TimeZone.getTimeZone("GMT"),
    ...

所以,显然,它实际上使用的是GMT,而不是默认的JVM默认值。

我想说最好的选择可能不是依赖于此而是自己在ObjectMapper.setTimeZone(TimeZone tz)上设置。

答案 1 :(得分:17)

UTC与GMT

对于商业应用,UTCGMT之间没有实际差异。唯一的区别与亚秒级分辨率和每隔几年增加Leap Second有关。对于科学,天文学,卫星跟踪和此类应用程序而言,差异可能很大,但这种情况很少见。

Jackson默认为UTC / GMT

我不知道 Jackson 。但是从查看你链接的文档来看,它们看起来像是序列化了(a)自1970年1月1日UTC以来的毫秒数,或者(b)字符串格式,默认为ISO 8601格式:“1970 -01-01T00:00:00.000 + 0000" 。因此,为了回答关于时区的问题,听起来默认情况下杰克逊总是使用UTC(没有时区偏移)进行序列化,这是正确的方法。如果您关心当时正在使用的时区,您应该在单独的字段中记录该事实(什么时区)。

杰克逊↔java.util.Date/Calendar↔Joda-Time

这两个序列化值(毫秒和ISO 8601字符串)都可以与Joda-Time DateTime实例的构造函数一起使用。

String dateTimeString = "2013-11-22T18:37:55.645+0000";
org.joda.time.DateTime myDateTime = org.joda.time.format.ISODateTimeFormat.dateTime().withZoneUTC().parseDateTime( dateTimeString );

和...

Long millisSinceEpoch = 1385495462L;
org.joda.time.DateTime myDateTime = new org.joda.time.DateTime( millisSinceEpoch );

如果您无法直接访问这些序列化值以提供给Joda-Time DateTime构造函数,那么让Jackson实例化java.util.Date/Calendar对象。将这些java.util.Date/Calendar对象提供给Joda-Time以实例化DateTime对象以进行进一步的工作。 Joda-Time用户通常会这样做。

org.joda.time.DateTime myDateTime = new org.joda.time.DateTime( someJavaUtilDateFromJackson );

您可以通过调用toDateTime()方法并传递所需的时区,轻松将该UTC时间转换为Joda-Time中的其他时区。

org.joda.time.DateTimeZone kolkataTimeZone = org.joda.time.DateTimeZone.forID( "Asia/Kolkata" );
org.joda.time.DateTime dateTimeInKolkata = myDateTime.toDateTime( kolkataTimeZone ); 

Joda-Time使用toDate方法轻松转换回java.util.Date。所以你在Joda-Time的大部分工作都要做,然后转换回java.util.Date与Jackson交流。当回到杰克逊时,我会将我的DateTimes切换回UTC,只是为了好的措施。

myDateTime.toDateTime( org.joda.time.DateTimeZone.UTC )

您可以在StackOverflow.com上找到许多上述Joda-Time操作的示例。

Just Do It

我怀疑你做得太担心而且编码不够。只需尝试一些小实验,将值传入和传出Jackson和Joda-Time。你很快就会掌握它。我建议你让Jackson默认做任何想做的事,然后在Joda-Time中操纵。 Joda-Time是针对日期时间的粗糙问题而建立的,而Jackson可能不是。 Joda-Time有两种构造函数和方法可以根据需要在时区之间进行调整。

更光明的未来

在Java 8中,JSR 310: Date and Time API将类似Joda-Time的类构建到Java平台中。期待看到像 Jackson 这样的框架更新为直接使用这些新类,同时弃用丑陋的java.util.Date/Calendar类。

看起来jackson-datatype-joda project正试图为Joda-Time带来这种便利。但这对我来说似乎没有必要。您可以在java.util.Date/Calendar和Joda-Time之间进行转换,如上所述。

P.S。该项目文档的“Wiki”链接失败。所以我无法查看他们的文档。

答案 2 :(得分:4)

我也在使用日期格式,最后我找到了解决方案。我想使用格式"2016-02-08T12:49:22.876Z",因为它是ISO 8601,它由JavaScript Date对象使用。我还想总是使用UTC时区。

我发现可以使用以下代码完成此操作:

final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setDateFormat(dateFormat);

System.out.println(objectMapper.writeValueAsString(new Date()));

请注意格式字符串中的X字符。它受Java 7支持,并在ISO 8601中指定时区。如SimpleDateFormat中所述,如果时区偏移为0(UTC),它将生成Z(而不是+00:00)。

答案 3 :(得分:1)

从一个简单的代码中我发现,当杰克逊从字符串反序列化为Date对象时,如果没有提到时区,则会给出UTC,但直接实例化后gives default timezone即机器的时区。

DateTime date = new DateTime(2013, 1, 2, 0, 0) //gives local timezone

以下给出了UTC

ObjectMapper mapper = new ObjectMapper();
DateTime dt = new DateTime(2013, 1, 2, 0, 0);
String serialized = mapper.writeValueAsString(dt);
DateTime dt1 = mapper.readValue(serialized, DateTime.class); //gives UTC