DateFormat pattern “yyyy-MM-dd'T'HH:mm:ss.SSS'Z'“ in Gson

2022-09-02 13:10:28

我有两个如下所示的字段(请注意,第一个字段具有毫秒部分):

{
  "updateTime":"2011-11-02T02:50:12.208Z",
  "deliverTime":"1899-12-31T16:00:00Z"
}

我想用Gson将Json字符串反序列化为对象,所以我得到一个实例:Gson

GsonBuilder gb = new GsonBuilder();
gb.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
gson = gb.create();

第一个字段被反序列化为Java日期类型:2011-11-02 02:50:12.208(看起来像忽略了时区部分 - 'Z',这是我预期的)。但是,第二个字段被反序列化为 1900-01-01 00:00:00(我住在中国,这里 +8 GMT),似乎时区部分 -'Z' 在反序列化中起作用。

为什么第二个字段使用时区部分?这不是我所期望的。


答案 1

快速解答

第一个字符串使用您的日期格式和本地时区正确解析,第二个字符串不尊重它,因此将由没有毫秒的默认对象解析(“yyyy-MM-dd'T'HH:mm:ss'Z'是解析格式),并使用UTC时区为您提供时间部分的”移位”。SimpleDateFormat

完整答案

要完全回答您的问题,您需要深入研究Gson源代码。更具体地说,您必须查看用于解析日期的代码。您可以在链接中找到所有这些代码,但为了快速参考,我将在这里复制最相关的部分。DefaultDateTypeAdapter

当您在构建器中调用此参数时:

gb.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

您正在以这种方式初始化 DefaultDateTypeAdapter:

DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) {
   this.enUsFormat = enUsFormat;
   this.localFormat = localFormat;
   this.iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
   this.iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
}

哪里:

  1. enUsFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
  2. localFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US)

自您在构建器中传递的字符串以来。

请注意,这不是一个时区,这与没有毫秒但使用UTC时区相同。Locale.USiso8601FormatenUsFormat

解析发生在方法中:deserializeToDate

 private Date deserializeToDate(JsonElement json) {
    synchronized (localFormat) {
      try {
        return localFormat.parse(json.getAsString());
      } catch (ParseException ignored) {
      }
      try {
        return enUsFormat.parse(json.getAsString());
      } catch (ParseException ignored) {
      }
      try {
        return iso8601Format.parse(json.getAsString());
      } catch (ParseException e) {
        throw new JsonSyntaxException(json.getAsString(), e);
      }
    }
  }

其中,所有三种日期格式都用于瀑布方法。

第一个 Json 字符串:“2011-11-02T02:50:12.208Z”。它立即解析,因为有毫秒,并为您提供使用时区预期的结果。localFormat

第二个 Json 字符串:“1899-12-31T16:00:00Z”。它不会被解析,因为没有毫秒,所以第二次机会是enUsFormat是相同的模式,除了区域设置。因此,它将以同样的方式失败。localFormat

解析的最后机会:,它将没有毫秒,但是,对于构造,它也是一个UTC时区,因此它将解析日期为UTC,而其他时间则使用您的时区进行解析。iso8601Format


答案 2

推荐