SimpleDateFormat Parse perd son fuseau horaire

Code:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z"); sdf.setTimeZone(TimeZone.getTimeZone("GMT")); System.out.println(new Date()); try { Ssortingng d = sdf.format(new Date()); System.out.println(d); System.out.println(sdf.parse(d)); } catch (Exception e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } 

Sortie:

 Thu Aug 08 17:26:32 GMT+08:00 2013 2013.08.08 09:26:32 GMT Thu Aug 08 17:26:32 GMT+08:00 2013 

Notez que format() formate correctement la Date en GMT, mais que parse() perdu les détails GMT. Je sais que je peux utiliser la subssortingng() et contourner ce problème, mais quelle est la raison de ce phénomène?

Voici une question en double qui n’a pas de réponse.

Edit: Permettez-moi de poser la question d’une autre manière, quelle est la manière de récupérer un object Date afin qu’il soit toujours dans GMT?

Tout ce dont j’avais besoin était ceci:

 SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); sdf.setTimeZone(TimeZone.getTimeZone("GMT")); SimpleDateFormat sdfLocal = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); try { Ssortingng d = sdf.format(new Date()); System.out.println(d); System.out.println(sdfLocal.parse(d)); } catch (Exception e) { e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. } 

Sortie: légèrement douteuse, mais je veux seulement que la date soit cohérente

 2013.08.08 11:01:08 Thu Aug 08 11:01:08 GMT+08:00 2013 

La solution d’OP à son problème, comme il le dit, a des résultats douteux. Ce code montre encore une confusion sur les représentations du temps. Pour dissiper cette confusion et créer un code qui ne conduise pas à des moments incorrects, considérez cette extension de ce qu’il a fait:

 public static void _testDateFormatting() { SimpleDateFormat sdfGMT1 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); sdfGMT1.setTimeZone(TimeZone.getTimeZone("GMT")); SimpleDateFormat sdfGMT2 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z"); sdfGMT2.setTimeZone(TimeZone.getTimeZone("GMT")); SimpleDateFormat sdfLocal1 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); SimpleDateFormat sdfLocal2 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss z"); try { Date d = new Date(); Ssortingng s1 = d.toSsortingng(); Ssortingng s2 = sdfLocal1.format(d); // Store s3 or s4 in database. Ssortingng s3 = sdfGMT1.format(d); Ssortingng s4 = sdfGMT2.format(d); // Resortingeve s3 or s4 from database, using LOCAL sdf. Ssortingng s5 = sdfLocal1.parse(s3).toSsortingng(); //EXCEPTION Ssortingng s6 = sdfLocal2.parse(s3).toSsortingng(); Ssortingng s7 = sdfLocal1.parse(s4).toSsortingng(); Ssortingng s8 = sdfLocal2.parse(s4).toSsortingng(); // Resortingeve s3 from database, using GMT sdf. // Note that this is the SAME sdf that created s3. Date d2 = sdfGMT1.parse(s3); Ssortingng s9 = d2.toSsortingng(); Ssortingng s10 = sdfGMT1.format(d2); Ssortingng s11 = sdfLocal2.format(d2); } catch (Exception e) { e.printStackTrace(); } } 

examiner les valeurs dans un débogueur:

 s1 "Mon Sep 07 06:11:53 EDT 2015" (id=831698113128) s2 "2015.09.07 06:11:53" (id=831698114048) s3 "2015.09.07 10:11:53" (id=831698114968) s4 "2015.09.07 10:11:53 GMT+00:00" (id=831698116112) s5 "Mon Sep 07 10:11:53 EDT 2015" (id=831698116944) s6 -- omitted, gave parse exception s7 "Mon Sep 07 10:11:53 EDT 2015" (id=831698118680) s8 "Mon Sep 07 06:11:53 EDT 2015" (id=831698119584) s9 "Mon Sep 07 06:11:53 EDT 2015" (id=831698120392) s10 "2015.09.07 10:11:53" (id=831698121312) s11 "2015.09.07 06:11:53 EDT" (id=831698122256) 

sdf2 et sdfLocal2 incluent le fuseau horaire, nous pouvons donc voir ce qui se passe réellement. s1 & s2 sont à 06:11:53 dans la zone EDT. s3 & s4 sont à 10:11:53 dans la zone GMT – équivalent à l’heure EDT d’origine. Imaginez que nous économisons s3 ou s4 dans une firebase database, où nous utilisons la GMT pour la cohérence, de sorte que nous pouvons avoir des temps de n’importe où dans le monde, sans stocker différents fuseaux horaires.

s5 parsing l’heure GMT, mais la traite comme une heure locale. Donc, il est dit “10:11:53” – l’heure GMT – mais pense qu’il est 10:11:53 à l’ heure locale . Pas bon.

s7 parsing l’heure GMT, mais ignore le GMT dans la chaîne, donc le traite toujours comme une heure locale.

S8 fonctionne, car nous incluons maintenant GMT dans la chaîne, et l’parsingur de zone locale l’utilise pour convertir un fuseau horaire en un autre.

Supposons maintenant que vous ne souhaitiez pas stocker la zone, vous voulez pouvoir parsingr s3, mais l’afficher comme heure locale. La réponse est d’ parsingr en utilisant le même fuseau horaire dans lequel il a été stocké – utilisez donc le même sdf que celui dans lequel il a été créé, sdfGMT1. s9, s10, & s11 sont toutes des représentations de l’heure d’origine. Ils sont tous “corrects”. C’est-à-dire, d2 == d1. Alors ce n’est qu’une question de comment vous voulez l’afficher. Si vous souhaitez afficher ce qui est stocké dans l’heure DB-GMT, vous devez le formater à l’aide d’un fichier sdf GMT. C’est s10.

Voici donc la solution finale, si vous ne souhaitez pas stocker explicitement avec “GMT” dans la chaîne et que vous souhaitez afficher au format GMT:

 public static void _testDateFormatting() { SimpleDateFormat sdfGMT1 = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss"); sdfGMT1.setTimeZone(TimeZone.getTimeZone("GMT")); try { Date d = new Date(); Ssortingng s3 = sdfGMT1.format(d); // Store s3 in DB. // ... // Resortingeve s3 from database, using GMT sdf. Date d2 = sdfGMT1.parse(s3); Ssortingng s10 = sdfGMT1.format(d2); } catch (Exception e) { e.printStackTrace(); } } 

tl; dr

Quelle est la manière de récupérer un object Date afin qu’il soit toujours en GMT?

 Instant.now() 

Détails

Vous utilisez d’anciennes classes date-heure déroutantes qui sont maintenant supplantées par les classes java.time.

Instant = UTC

La classe Instant représente un moment sur la timeline en UTC avec une résolution de nanosecondes (jusqu’à neuf (9) chiffres d’une fraction décimale).

 Instant instant = Instant.now() ; // Current moment in UTC. 

ISO 8601

Pour échanger ces données sous forme de texte, utilisez exclusivement les formats standard ISO 8601 . Ces formats sont judicieusement conçus pour être non ambigus, faciles à traiter par machine et faciles à lire dans de nombreuses cultures.

Les classes java.time utilisent les formats standard par défaut lors de l’parsing et de la génération de chaînes.

 Ssortingng output = instant.toSsortingng() ; 

2017-01-23T12: 34: 56.123456789Z

Fuseau horaire

Si vous voulez voir le même moment que celui présenté dans l’horloge d’une région particulière, appliquez un ZoneId pour obtenir un ZonedDateTime .

Spécifiez un nom de fuseau horaire approprié dans le format de continent/region , tel que America/Montreal , Africa/Casablanca ou Pacific/Auckland . N’utilisez jamais l’abréviation de 3-4 lettres comme EST ou IST car ce ne sont pas de véritables fuseaux horaires, non standardisés, et même pas uniques (!).

 ZoneId z = ZoneId.of( "Asia/Singapore" ) ; ZonedDateTime zdt = instant.atZone( z ) ; // Same simultaneous moment, same point on the timeline. 

Voir ce code en direct sur IdeOne.com .

Notez la différence de huit heures, car le fuseau horaire de l’ Asia/Singapore actuellement un décalage par rapport à l’UTC de +08: 00. Même moment, heure de l’horloge différente.

instant.toSsortingng (): 2017-01-23T12: 34: 56.123456789Z

zdt.toSsortingng (): 2017-01-23T20: 34: 56.123456789 + 08: 00 [Asie / Singapour]

Convertir

Évitez la classe java.util.Date héritée. Mais si vous devez, vous pouvez convertir. Regardez les nouvelles méthodes ajoutées aux anciennes classes.

 java.util.Date date = Date.fromInstant( instant ) ; 

… Aller dans l’autre sens…

 Instant instant = myJavaUtilDate.toInstant() ; 

Date uniquement

Pour la date uniquement, utilisez LocalDate .

 LocalDate ld = zdt.toLocalDate() ; 

À propos de java.time

Le framework java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciennes classes de date / heure héritées telles que java.util.Date , Calendar et SimpleDateFormat .

Le projet Joda-Time , maintenant en mode maintenance , conseille la migration vers les classes java.time .

Pour en savoir plus, consultez le didacticiel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .

Où obtenir les classes java.time?

  • Java SE 8 , Java SE 9 et versions ultérieures
    • Intégré
    • Partie de l’API Java standard avec une implémentation intégrée.
    • Java 9 ajoute quelques fonctionnalités et corrections mineures.
  • Java SE 6 et Java SE 7
    • Une grande partie de la fonctionnalité java.time est transférée vers Java 6 et 7 dans ThreeTen-Backport .
  • Android
    • Le projet ThreeTenABP adapte ThreeTen-Backport (mentionné ci-dessus) pour Android en particulier.
    • Voir Comment utiliser ThreeTenABP… .

Le projet ThreeTen-Extra étend java.time avec des classes supplémentaires. Ce projet est un terrain d’essai pour d’éventuels ajouts futurs à java.time. Vous pouvez y trouver des classes utiles telles que Interval , YearWeek , YearQuarter , etc.