Java convertir GMT/UTC en heure locale ne fonctionne pas comme prévu

afin de montrer un scénario reproductible, je fais ce qui suit

  1. Obtenir l'heure actuelle du système (heure locale)

  2. Convertissez L'heure locale en UTC / / Works Fine Till here

  3. Inverser L'heure UTC, retour à l'heure locale. A suivi 3 approches différentes (énumérées ci-dessous) mais toutes les 3 approches conserve le temps en UTC seulement.

    {

    long ts = System.currentTimeMillis();
    Date localTime = new Date(ts);
    String format = "yyyy/MM/dd HH:mm:ss";
    SimpleDateFormat sdf = new SimpleDateFormat (format);
    
    // Convert Local Time to UTC (Works Fine) 
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date gmtTime = new Date(sdf.format(localTime));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
    
    // Reverse Convert UTC Time to Locale time (Doesn't work) Approach 1
    sdf.setTimeZone(TimeZone.getDefault());        
    localTime = new Date(sdf.format(gmtTime));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
    
    // Reverse Convert UTC Time to Locale time (Doesn't work) Approach 2 using DateFormat
    DateFormat df = new SimpleDateFormat (format);
    df.setTimeZone(TimeZone.getDefault());
    localTime = df.parse((df.format(gmtTime)));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime());
    
    // Approach 3
    Calendar c = new GregorianCalendar(TimeZone.getDefault());
    c.setTimeInMillis(gmtTime.getTime());
    System.out.println("Local Time " + c.toString());
    

    }

28
demandé sur Vinod Jayachandran 2013-10-15 11:19:24

6 réponses

je recommande également d'utiliser Joda comme mentionné ci-dessus.

résoudre votre problème en utilisant la norme Java Date les objets seulement peuvent être faits comme suit:

    // **** YOUR CODE **** BEGIN ****
    long ts = System.currentTimeMillis();
    Date localTime = new Date(ts);
    String format = "yyyy/MM/dd HH:mm:ss";
    SimpleDateFormat sdf = new SimpleDateFormat(format);

    // Convert Local Time to UTC (Works Fine)
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date gmtTime = new Date(sdf.format(localTime));
    System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:"
            + gmtTime.toString() + "," + gmtTime.getTime());

    // **** YOUR CODE **** END ****

    // Convert UTC to Local Time
    Date fromGmt = new Date(gmtTime.getTime() + TimeZone.getDefault().getOffset(localTime.getTime()));
    System.out.println("UTC time:" + gmtTime.toString() + "," + gmtTime.getTime() + " --> Local:"
            + fromGmt.toString() + "-" + fromGmt.getTime());

sortie:

Local:Tue Oct 15 12:19:40 CEST 2013,1381832380522 --> UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000
UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000 --> Local:Tue Oct 15 12:19:40 CEST 2013-1381832380000
56
répondu trylimits 2017-11-12 21:18:38

Joda-Time


mise à JOUR: Le Joda-Time le projet est maintenant dans mode maintenance , avec l'équipe conseiller de la migration vers le de java.heure classes. Voir Tutoriel par Oracle .

Voir mon autre Réponse à l'aide de la pointe de l'industrie de java.temps classes.


Normalement nous le considérons en mauvaise forme sur StackOverflow.com répondre à une question précise en suggérant une autre technologie. Mais dans le cas des classes de date, d'heure et de calendrier empaquetées avec Java 7 et les précédentes, ces classes sont si notoirement mauvaises dans la conception et l'exécution que je suis obligé de suggérer l'utilisation d'une bibliothèque tierce partie à la place: Joda-Time .

Joda-Time fonctionne en créant des objets immuables . Ainsi, plutôt que de modifier le fuseau horaire d'un objet DateTime, nous instancions simplement un nouveau DateTime avec un fuseau horaire différent assigné.

votre préoccupation centrale d'utiliser à la fois l'heure locale et L'heure TUC est si simple dans Joda-Time, ne prenant que 3 lignes de code.

    org.joda.time.DateTime now = new org.joda.time.DateTime();
    System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
    System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );

sortie lorsque l'exécution sur la côte ouest de L'Amérique du Nord pourrait être:

Local time in ISO 8601 format: 2013-10-15T02:45:30.801-07:00

UTC (Zulu) time zone: 2013-10-15T09:45:30.801Z

Voici une classe avec plusieurs exemples et d'autres commentaires. En Utilisant Joda-Time 2.5.

/**
 * Created by Basil Bourque on 2013-10-15.
 * © Basil Bourque 2013
 * This source code may be used freely forever by anyone taking full responsibility for doing so.
 */
public class TimeExample {
    public static void main(String[] args) {
        // Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 8 and earlier.
        // http://www.joda.org/joda-time/

        // Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
        // JSR 310 was inspired by Joda-Time but is not directly based on it.
        // http://jcp.org/en/jsr/detail?id=310

        // By default, Joda-Time produces strings in the standard ISO 8601 format.
        // https://en.wikipedia.org/wiki/ISO_8601
        // You may output to strings in other formats.

        // Capture one moment in time, to be used in all the examples to follow.
        org.joda.time.DateTime now = new org.joda.time.DateTime();

        System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
        System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );

        // You may specify a time zone in either of two ways:
        // • Using identifiers bundled with Joda-Time
        // • Using identifiers bundled with Java via its TimeZone class

        // ----|  Joda-Time Zones  |---------------------------------

        // Time zone identifiers defined by Joda-Time…
        System.out.println( "Time zones defined in Joda-Time : " + java.util.Arrays.toString( org.joda.time.DateTimeZone.getAvailableIDs().toArray() ) );

        // Specify a time zone using DateTimeZone objects from Joda-Time.
        // http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTimeZone.html
        org.joda.time.DateTimeZone parisDateTimeZone = org.joda.time.DateTimeZone.forID( "Europe/Paris" );
        System.out.println( "Paris France (Joda-Time zone): " + now.toDateTime( parisDateTimeZone ) );

        // ----|  Java Zones  |---------------------------------

        // Time zone identifiers defined by Java…
        System.out.println( "Time zones defined within Java : " + java.util.Arrays.toString( java.util.TimeZone.getAvailableIDs() ) );

        // Specify a time zone using TimeZone objects built into Java.
        // http://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html
        java.util.TimeZone parisTimeZone = java.util.TimeZone.getTimeZone( "Europe/Paris" );
        System.out.println( "Paris France (Java zone): " + now.toDateTime(org.joda.time.DateTimeZone.forTimeZone( parisTimeZone ) ) );

    }
}
4
répondu Basil Bourque 2018-06-17 07:51:54

je me joins à la chorale vous recommandant de sauter les classes maintenant dépassées Date , Calendar , SimpleDateFormat et amis. En particulier, je voudrais mettre en garde contre l'utilisation des méthodes et des constructeurs dépréciés de la classe Date , comme le constructeur Date(String) que vous avez utilisé. Ils ont été dépréciés parce qu'ils ne fonctionnent pas de manière fiable à travers les fuseaux horaires, donc ne les utilisez pas. Et oui, la plupart des constructeurs et des méthodes de cette classe sont obsolètes.

alors qu'à l'époque où vous avez posé la question, Joda-Time était (de ce que je sais) une alternative nettement meilleure, le temps est passé à nouveau. Aujourd'hui Joda-Time est un projet largement terminé, et ses développeurs vous recommandent d'utiliser java.time , L'API Java date et time moderne, à la place. Je vais vous montrer comment.

    ZonedDateTime localTime = ZonedDateTime.now(ZoneId.systemDefault());

    // Convert Local Time to UTC 
    OffsetDateTime gmtTime
            = localTime.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
    System.out.println("Local:" + localTime.toString() 
            + " --> UTC time:" + gmtTime.toString());

    // Reverse Convert UTC Time to Local time
    localTime = gmtTime.atZoneSameInstant(ZoneId.systemDefault());
    System.out.println("Local Time " + localTime.toString());

pour commencer, notez que non seulement le code est moitié moins long que le vôtre, il est aussi plus clair à lire.

sur mon ordinateur le code imprime:

Local:2017-09-02T07:25:46.211+02:00[Europe/Berlin] --> UTC time:2017-09-02T05:25:46.211Z
Local Time 2017-09-02T07:25:46.211+02:00[Europe/Berlin]

j'ai oublié les millisecondes de l'époque. Vous pouvez toujours les obtenir de System.currentTimeMillis(); comme dans votre question, et ils sont indépendants du fuseau horaire, donc je ne les ai pas trouvés interstitiels ici.

j'ai hésité à garder votre nom de variable localTime . Je pense que c'est un bon nom. L'API moderne a une classe appelée LocalTime , donc en utilisant ce nom, seulement non capitalisé, pour un objet qui n'a pas le type LocalTime il peut y avoir confusion (un LocalTime ne contient pas d'information sur les fuseaux horaires, que nous devons garder ici pour pouvoir faire la bonne conversion; il contient aussi seulement l'heure du jour, pas la date).

votre conversion de L'heure locale à L'UTC était incorrecte et impossible

la classe périmée Date ne contient aucune information sur les fuseaux horaires (vous pouvez dire qu'elle utilise toujours UTC à l'interne), donc il n'y a pas de conversion d'un Date d'un fuseau horaire à un autre. Quand j'ai lancé votre code sur mon ordinateur, la première ligne qu'il a imprimée était:

Local:Sat Sep 02 07:25:45 CEST 2017,1504329945967 --> UTC time:Sat Sep 02 05:25:45 CEST 2017-1504322745000

07:25:45 CEST est correct, bien sûr. L'heure UTC correcte aurait été 05:25:45 UTC , mais elle dit CEST encore une fois, ce qui est incorrect.

Maintenant vous n'aurez plus jamais besoin de la classe Date ,: -) mais si jamais vous alliez à, le must-read serait tout au sujet Java.util.Date sur le blog codant de Jon Skeet.

Question: Puis-je utiliser L'API moderne avec ma version Java?

si vous utilisez au moins Java 6 , vous pouvez.

2
répondu Ole V.V. 2017-09-02 05:33:22

je recommande fortement D'utiliser le temps Joda http://joda-time.sourceforge.net/faq.html

1
répondu Tomasz Waszczyk 2013-10-15 07:21:28

vous avez une date avec un fuseau horaire connu (ici Europe/Madrid ), et un fuseau horaire cible ( UTC )

vous avez juste besoin de deux formulaires simples:

        long ts = System.currentTimeMillis();
        Date localTime = new Date(ts);

        SimpleDateFormat sdfLocal = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss");
        sdfLocal.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));

        SimpleDateFormat sdfUTC = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss");
        sdfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));

        // Convert Local Time to UTC
        Date utcTime = sdfLocal.parse(sdfUTC.format(localTime));
        System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + utcTime.toString() + "-" + utcTime.getTime());

        // Reverse Convert UTC Time to Locale time
        localTime = sdfUTC.parse(sdfLocal.format(utcTime));
        System.out.println("UTC:" + utcTime.toString() + "," + utcTime.getTime() + " --> Local time:" + localTime.toString() + "-" + localTime.getTime());

donc après avoir vu comment ça marche, vous pouvez ajouter cette méthode à vos utils:

    public Date convertDate(Date dateFrom, String fromTimeZone, String toTimeZone) throws ParseException {
        String pattern = "yyyy/MM/dd HH:mm:ss";
        SimpleDateFormat sdfFrom = new SimpleDateFormat (pattern);
        sdfFrom.setTimeZone(TimeZone.getTimeZone(fromTimeZone));

        SimpleDateFormat sdfTo = new SimpleDateFormat (pattern);
        sdfTo.setTimeZone(TimeZone.getTimeZone(toTimeZone));

        Date dateTo = sdfFrom.parse(sdfTo.format(dateFrom));
        return dateTo;
    }
1
répondu albfan 2016-08-01 09:04:26

tl; dr

Instant.now()                           // Capture the current moment in UTC.
.atZone( ZoneId.systemDefault() )       // Adjust into the JVM's current default time zone. Same moment, different wall-clock time. Produces a `ZonedDateTime` object.
.toInstant()                            // Extract a `Instant` (always in UTC) object from the `ZonedDateTime` object.
.atZone( ZoneId.of( "Europe/Paris" ) )  // Adjust the `Instant` into a specific time zone. Renders a `ZonedDateTime` object. Same moment, different wall-clock time.
.toInstant()                            // And back to UTC again.

de java.time

l'approche moderne utilise le java.les classes de temps qui supplantent les anciennes classes de date-temps ( Date , Calendar , etc.).

votre utilisation du mot "local" contredit l'usage dans le java.classe de temps . Dans java.temps , "local" signifie tout localité ou toutes les localités , mais pas une localité particulière. Le java.les classes de temps avec des noms commençant par" Local... " n'ont aucune notion de fuseau horaire ou de décalage-par-UTC. Donc ils font et non représentent un moment précis, ils sont et non un point sur la ligne de temps, alors que votre Question est tout au sujet des moments, des points sur la ligne de temps vu à travers différents temps d'horloge murale.

Obtenir l'heure actuelle du système (heure locale)

si vous voulez capturer le moment actuel dans UTC, utilisez Instant . La classe Instant représente un moment sur la ligne de temps dans UTC avec une résolution de nanosecondes (jusqu'à neuf (9) chiffres d'une fraction décimale).

Instant instant = Instant.now() ;  // Capture the current moment in UTC.

S'ajuster à un fuseau horaire en appliquant un ZoneId pour obtenir un ZonedDateTime . Même moment, même point sur la ligne de temps, heure différente.

spécifier un nom du 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 ils sont pas vrai fuseaux horaires, pas standardisé, et même pas unique(!).

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, different wall-clock time.

Comme un raccourci, vous pouvez sauter l'utilisation de Instant pour obtenir un ZonedDateTime .

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;

convertir L'heure locale en UTC / / fonctionne très bien Jusqu'à ici

vous pouvez ajuster de la date-heure zonée à UTC en extrayant un Instant d'un ZonedDateTime .

ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Instant instant = zdt.toInstant() ;

Inverser L'UTC temps, de retour à l'heure locale.

Comme indiqué ci-dessus, appliquer un ZoneId pour ajuster le même moment dans une autre horloge murale utilisée par les gens d'une certaine région (fuseau horaire).

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

ZoneId zDefault = ZoneId.systemDefault() ;  // The JVM's current default time zone.
ZonedDateTime zdtDefault = instant.atZone( zDefault ) ;

ZoneId zTunis = ZoneId.of( "Africa/Tunis" ) ;  // The JVM's current default time zone.
ZonedDateTime zdtTunis = instant.atZone( zTunis ) ;

ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;  // The JVM's current default time zone.
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;

pour retourner à UTC à partir d'une date-heure zonée, appelez ZonedDateTime::toInstant . Pensez-y conceptuellement comme: ZonedDateTime = Instant + ZoneId .

Instant instant = zdtAuckland.toInstant() ;

tous ces objets, le Instant et les trois ZonedDateTime objets représentent tous le même moment simultané, le même point dans l'histoire.

a suivi trois approches différentes (énumérées ci-dessous), mais toutes les trois conservent le temps en UTC seulement.

oubliez d'essayer de fixer le code en utilisant ces terribles Date , Calendar , et GregorianCalendar classes. Ils sont un gâchis misérable de mauvais design et de défauts. Vous avez besoin de ne jamais toucher de nouveau. Si vous devez interface avec l'ancien code pas encore mis à jour en java.temps , vous pouvez convertir en va-et-vient via de nouvelles méthodes de conversion ajoutées aux anciennes classes.


à Propos de de java.heure

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

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

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

vous pouvez échanger java.temps objets directement avec votre base de données. Utiliser un JDBC driver conforme à JDBC 4.2 ou plus tard. Pas besoin de Chaînes, pas besoin de classes java.sql.* .

où pour obtenir le java.les classes de temps?

le projet ThreeTen-Extra é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 .

0
répondu Basil Bourque 2018-06-17 07:46:12