18

以下のような 2 つのフィールドがあります (最初のフィールドにはミリ秒のセクションがあることに注意してください)。

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

Json 文字列を Gson を使用してオブジェクトに逆シリアル化したいので、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」が無視されているように見えますが、これは予想どおりです)。ただし、2 番目のフィールドは1900-01-01 00:00:00に逆シリアル化されます(私は中国に住んでいます。ここでは +8 GMT です)。タイム ゾーン セクション「Z」が逆シリアル化に関与しているようです。

2 番目のフィールドでタイム ゾーン セクションが使用されるのはなぜですか? それは私が期待したものではありません。

4

1 に答える 1

22

素早い回答

最初の文字列は日付形式とローカル タイム ゾーンを使用して正しく解析されますが、2 番目の文字列はそれを尊重しないため、SimpleDateFormatミリ秒を持たない既定のオブジェクトによって解析されます ("yyyy-MM-dd'T'HH:mm:ss' Z' は解析形式) であり、UTC タイムゾーンを使用して時間部分の「シフト」を提供します。

完全な答え

あなたの質問に完全に答えるには、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)

ビルダーに渡した文字列から。

Locale.USこれはタイムゾーンではなく、ミリ秒がなくても UTC タイムゾーンがiso8601Formatある場合と同じであることに注意してください。enUsFormat

解析は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);
      }
    }
  }

ここでは、3 つの日付形式すべてがウォーターフォール アプローチで使用されています。

最初の Json 文字列: "2011-11-02T02:50:12.208Z"。ミリ秒単位なので、すぐに解析されlocalFormat、タイムゾーンを使用して期待する結果が得られます。

2 番目の Json 文字列: "1899-12-31T16:00:00Z"。ミリ秒がないため解析されないため、ロケールを除いて同じパターンlocalFormatである enUsFormat が 2 番目のチャンスです。したがって、同じように失敗します。

解析する最後のチャンス: iso8601Format、ミリ秒はありませんが、構築のために UTC タイムゾーンとしても使用されるため、日付を UTC として解析し、他の人はあなたのタイムゾーンを使用して解析します。

于 2013-09-01T22:16:08.667 に答える