web-dev-qa-db-ja.com

GsonのDateFormatパターン "yyyy-MM-dd'T'HH:mm:ss.SSS'Z '"

以下のような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番目のフィールドがタイムゾーンセクションを使用する理由それは私が期待したものではありません。

11
Domi.Zhang

簡単な回答

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

完全な回答

質問に完全に回答するには、Gsonのソースコードを詳しく調べる必要があります。特に、日付の解析に使用されるDefaultDateTypeAdapterのコードを確認する必要があります。このコードはすべて link にありますが、クイックリファレンスとして、最も関連性の高い部分をここにコピーします。

これをビルダーで呼び出すと:

_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_はタイムゾーンではなく、_iso8601Format_はenUsFormatと同じで、ミリ秒はありませんがUTCタイムゾーンがあります。

解析は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によって解析されないため、ロケールを除いて、2番目のチャンスはと同じパターンであるenUsFormatです。したがって、同じように失敗します。

解析する最後のチャンス:_iso8601Format_、そうです、ミリ秒はありません、[〜#〜]しかし[〜#〜]、他のユーザーがあなたのタイムゾーンを使用して解析している間、それはUTCタイムゾーンとしても日付を解析します。

15
giampaolo