SimpleDateFormat parse perd le 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 {
        String 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

Note que format() formats Date correctement GMT, mais parse() perdu le GMT de détails. Je sais que je peux utiliser substring() et travailler autour de cela, mais quelle est la raison sous-jacente à ce phénomène?

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

Edit: laissez-moi poser la question d'une autre manière, Quelle est la façon de récupérer un objet Date pour qu'il soit toujours au format GMT?

47
demandé sur Community 2013-08-08 13:35:46

3 réponses

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 {
    String 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 que seule la date d'être cohérente

2013.08.08 11:01:08
Thu Aug 08 11:01:08 GMT+08:00 2013
44
répondu Achow 2017-04-25 08:45:39

la solution D'OP à son problème, comme il le dit, a un rendement douteux. Ce code révèle encore une certaine confusion au sujet des représentations du temps. Pour dissiper cette confusion, et faire du code qui ne conduira pas à des temps erronés, 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();
        String s1 = d.toString();
        String s2 = sdfLocal1.format(d);
        // Store s3 or s4 in database.
        String s3 = sdfGMT1.format(d);
        String s4 = sdfGMT2.format(d);
        // Retrieve s3 or s4 from database, using LOCAL sdf.
        String s5 = sdfLocal1.parse(s3).toString();
        //EXCEPTION String s6 = sdfLocal2.parse(s3).toString();
        String s7 = sdfLocal1.parse(s4).toString();
        String s8 = sdfLocal2.parse(s4).toString();
        // Retrieve s3 from database, using GMT sdf.
        // Note that this is the SAME sdf that created s3.
        Date d2 = sdfGMT1.parse(s3);
        String s9 = d2.toString();
        String s10 = sdfGMT1.format(d2);
        String s11 = sdfLocal2.format(d2);
    } catch (Exception e) {
        e.printStackTrace();
    }       
}

l'examen des 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, donc nous pouvons voir ce qui se passe réellement. s1 et s2 sont à 06:11:53 dans la zone EDT. s3 et s4 sont à 10: 11: 53 dans la zone GMT -- équivalent à l'heure EDT originale. Imaginez que nous sauvions s3 ou s4 dans une base de données, où nous utilisons 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 parse L'heure GMT, mais la traite comme une heure locale. Donc il dit "10: 11:53" -- l'heure GMT -- mais il pense qu'il est 10:11: 53 dans locale heure. Pas bonne.

S7 parle de L'heure GMT, mais ignore le GMT dans la chaîne, donc traite toujours comme une heure locale.

s8 fonctionne, parce que nous incluons maintenant GMT dans la chaîne, et de la zone locale de l'analyseur utilise pour convertir à partir d'un fuseau horaire à un autre.

supposons maintenant que vous ne voulez pas stocker la zone, vous voulez pouvoir analyser s3, mais l'afficher comme une heure locale. La réponse est à parse en utilisant le même fuseau horaire qu'il était stocké dans -- donc utilisez le même sdf qu'il a été créé dans, sdfGMT1. s9, s10, & s11 sont tous des représentations de l'origine du temps. Ils sont tous"corrects". Autrement dit, d2 == d1. Alors, ce n'est qu'une question de comment vous voulez L'afficher. Si vous voulez afficher ce qui est stocké en DB -- heure GMT -- alors vous devez le formater en utilisant un sdf GMT. Ths est s10.

donc voici la solution finale, si vous ne voulez pas explicitement stocker avec "GMT" dans la chaîne, et que vous voulez 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();
        String s3 = sdfGMT1.format(d);
        // Store s3 in DB.
        // ...
        // Retrieve s3 from database, using GMT sdf.
        Date d2 = sdfGMT1.parse(s3);
        String s10 = sdfGMT1.format(d2);
    } catch (Exception e) {
        e.printStackTrace();
    }       
}
7
répondu ToolmakerSteve 2015-09-07 10:51:44

tl; dr

comment récupérer un objet Date pour qu'il soit toujours au format GMT?

Instant.now() 

détails

vous utilisez des anciennes classes de date confuses et troublantes qui sont maintenant supplantées par le java.les classes de temps.

Instant = UTC

la classe Instant représente un moment sur la montage 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 de la norme ISO 8601 . Ces formats sont raisonnablement conçus pour être sans ambiguïté, faciles à traiter à la machine et faciles à lire dans de nombreuses cultures par les gens.

La java.les classes de temps utilisent les formats standards par défaut lors de l'analyse et de la génération de chaînes.

String output = instant.toString() ;  

2017-01-23T12: 34 :56.123456789 Z

fuseau horaire

si vous voulez voir ce même moment que présenté dans l'Heure de l'horloge murale d'une région particulière, appliquer un ZoneId pour obtenir un ZonedDateTime .

spécifiez un fuseau horaire approprié nom dans le format 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 ils sont pas vrai fuseaux horaires, non standardisé, et même pas unique(!).

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

voir ce IdeOne.com .

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

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

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

Convertir

Éviter l'héritage java.util.Date de la classe. Mais si vous doit, vous pouvez convertir des. Rechercher de 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

pour la date seulement, utiliser LocalDate .

LocalDate ld = zdt.toLocalDate() ;

à Propos de java.heure

Le de java.time framework est construit dans Java 8 et plus tard. Ces classes supplantent les vieux héritage date-heure classes telles que java.util.Date , Calendar , & SimpleDateFormat .

le Joda-Time project, maintenant en mode maintenance , conseille la migration vers le java.temps des classes.

pour en savoir plus, voir le tutoriel Oracle . Et rechercher le débordement de la pile pour de nombreux exemples et explications. La spécification est JSR 310 .

où obtenir le java.les classes de temps?

Le ThreeTen-Extra le projet s'étend java.temps avec des cours supplémentaires. Ce projet est un terrain d'expérimentation pour d'éventuelles futures additions à java.temps. Vous pouvez trouver ici quelques cours utiles tels que: Interval , YearWeek , YearQuarter , et plus .

5
répondu Basil Bourque 2017-09-04 00:43:20