web-dev-qa-db-fra.com

Format DateFormat "aaaa-MM-jj'T'HH: mm: ss.SSS'Z '" dans Gson

J'ai deux champs comme ci-dessous (attention, le premier champ a une section en millisecondes):

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

Je veux désérialiser la chaîne Json en un objet avec Gson, donc j'obtiens une instance Gson:

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

Le premier champ est désérialisé à Java type de date: 2011-11-02 02: 50: 12.208 (ressemble à ignoré la section de fuseau horaire-'Z ', c'est ce à quoi je m'attendais). Cependant, le deuxième champ est désérialisé en 1900-01-01 00:00:00 (J'habite en Chine, +8 GMT ici), il semble que la section de fuseau horaire-'Z 'joue un rôle dans la désérialisation.

Pourquoi le deuxième champ utilise la section de fuseau horaire? Ce n'est pas ce à quoi je m'attendais.

11
Domi.Zhang

Réponse rapide

La première chaîne est correctement analysée en utilisant votre format de date et votre fuseau horaire local, la seconde ne le respecte pas, elle sera donc analysée par un objet SimpleDateFormat par défaut qui n'a pas des millisecondes ("aaaa-MM-jj'T ' HH: mm: ss'Z 'est le format d'analyse) et utilise le fuseau horaire UTC vous donnant un "décalage" dans la partie temporelle.

Réponse complète

Pour répondre pleinement à votre question, vous devez plonger dans le code source de Gson. Plus particulièrement, vous devez regarder le code de DefaultDateTypeAdapter qui est utilisé pour analyser les dates. Vous pouvez trouver tout ce code sur lien , mais pour référence rapide, je vais copier ici les parties les plus pertinentes.

Lorsque vous appelez cela dans le générateur:

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

vous initialisez un DefaultDateTypeAdapter de cette façon:

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"));
}

où:

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

depuis la chaîne que vous avez passée dans le générateur.

Faites attention que Locale.US N'est pas un fuseau horaire et que iso8601Format Est identique à enUsFormat sans millisecondes mais avec le fuseau horaire UTC.

L'analyse se fait dans la méthode 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);
      }
    }
  }

où les trois formats de date sont utilisés dans une approche en cascade.

Première chaîne Json: "2011-11-02T02: 50: 12.208Z". Il est analysé immédiatement par localFormat car il a des millisecondes et vous donne le résultat que vous attendez en utilisant votre fuseau horaire.

Deuxième chaîne Json: "1899-12-31T16: 00: 00Z". Il ne sera pas analysé par localFormat car il n'a pas de millisecondes, donc la deuxième chance est enUsFormat qui est le même modèle, à l'exception des paramètres régionaux. Il échouera donc de la même manière.

Dernière chance d'analyser: iso8601Format, Il le fera, il n'a pas de millisecondes, [~ # ~] mais [~ # ~] , pour la construction, il s'agit également d'un fuseau horaire UTC, il analysera donc la date en UTC tandis que les autres analyseront en utilisant votre fuseau horaire.

15
giampaolo