25

al fine di mostrare uno scenario riproducibile, sto facendo il seguenteJava Conversione GMT/UTC in ora locale non funziona come previsto

  1. Prendi l'ora di sistema corrente (ora locale)

  2. Converti ora locale in UTC // Funziona bene fino a qui

  3. Invertire l'ora UTC, indietro all'ora locale. Sono seguiti 3 diversi approcci (elencati di seguito), ma tutti e 3 gli approcci mantengono solo il tempo in UTC.

    {

    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()); 
    

    }

risposta

51

Consiglio anche usando Joda come accennato prima.

risolvere il tuo problema utilizzando standard di JavaDate oggetti solo può essere fatto come segue:

// **** 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()); 

uscita:

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 
+3

Una buona risposta! Tuttavia, invece di TimeZone.getDefault(). GetOffset (localTime.getTime()), ci dovrebbero essere TimeZone.getDefault(). GetOffset (gmtTime.getTimeInMillis()). In caso contrario, potresti riscontrare problemi con DaylightSaving se il tuo orario UTC è in inverno, ma il locale è in estate. – Dime

+3

'new Date (stringa di stringhe)' deprecated :( –

+0

sei un life saver T_T questo è l'unico modo per risolvere i conflitti di java.util.date (@temporal) e timestamp (postgresql) di GMT rispetto a UTC – Sarief

1

vi consiglio vivamente di utilizzare Joda Tempo http://joda-time.sourceforge.net/faq.html

+0

Potete fornire un esempio per ottenere lo stesso utilizzando #joda? –

+0

Non mi interessa usare joda, ma vorrei capire perché il codice non si comporta come previsto. –

5

Normalmente si considerano cattiva modulo a StackOverflow.com per rispondere a una domanda specifica, suggerendo un supplente tecnologia. Ma nel caso delle classi data, ora e calendario in bundle con Java 7 e precedenti, quelle classi sono così notoriamente cattive sia nel design che nell'esecuzione che sono costretto a suggerire di utilizzare invece una libreria di terze parti: Joda-Time.

Joda-Time funziona creando immutable objects. Quindi, piuttosto che modificare il fuso orario di un oggetto DateTime, semplicemente istanziamo un nuovo DateTime con un fuso orario diverso assegnato.

Il tuo interesse principale per l'utilizzo dell'ora locale e dell'ora UTC è molto semplice in Joda-Time, prendendo solo 3 righe di codice.

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

uscita quando eseguito sulla costa occidentale del Nord America potrebbe essere:

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

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

Ecco una classe con numerosi esempi e ulteriori commenti. Utilizzo di 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))); 

    } 
} 
1

Lei ha un appuntamento con un fuso orario noto (Qui Europe/Madrid), e un fuso orario target (UTC)

Hai solo bisogno di due SimpleDateFormats:

 
     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()); 

Così, dopo vederlo lavorare è possibile aggiungere questo metodo per i tuoi 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; 
    } 
2

Mi iscrivo al coro nel quale si consiglia di saltare le classi ormai da tempo obsolete Date, Calendar, SimpleDateFormat e amici. In particolare, evito di utilizzare i metodi e i costruttori obsoleti della classe Date, come il costruttore Date(String) utilizzato. Sono stati deprecati perché non funzionano in modo affidabile tra fusi orari, quindi non usarli. E sì, la maggior parte dei costruttori e dei metodi di quella classe sono deprecati.

Mentre al momento della domanda, Joda-Time era (da tutto ciò che so) un'alternativa chiaramente migliore, il tempo è passato di nuovo. Oggi Joda-Time è un progetto in gran parte finito, e i suoi sviluppatori raccomandano l'uso di java.time, la moderna API di data e ora Java, invece. Ti mostrerò come.

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()); 

Per cominciare, si noti che non solo è il codice solo la metà il tempo come la tua, è anche più chiaro da leggere.

Sul mio computer le stampe codice:

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] 

ho lasciato fuori i millisecondi dall'epoca. Puoi sempre prenderli da System.currentTimeMillis(); come nella tua domanda, e sono indipendenti dal fuso orario, quindi non li ho trovati interessanti qui.

Ho indugiato esitante il nome della variabile localTime. Penso che sia un buon nome. La moderna API ha una classe chiamata LocalTime, quindi l'utilizzo di tale nome, non solo in maiuscolo, per un oggetto che non ha il tipo LocalTime potrebbe confondere alcuni (uno LocalTime non contiene le informazioni sul fuso orario, che dobbiamo mantenere qui per essere in grado di effettuare la conversione corretta, ma tiene solo l'ora del giorno, non la data).

vostra conversione da ora locale di UTC non era corretto e impossibile

Il obsoleto Date classe non detiene alcuna informazioni sul fuso orario (si può dire che internamente usa sempre UTC), quindi non c'è nessuna tale cosa come convertire un Date da un fuso orario all'altro. Quando ho appena eseguito il codice sul mio computer, la prima linea è stampato, era:

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 è corretto, naturalmente.L'ora UTC corretta sarebbe stata 05:25:45 UTC, ma si dice ancora CEST, che non è corretto.

Ora non avrai più bisogno della classe Date, :-) ma se mai dovessi andare, la lettura obbligata sarebbe All about java.util.Date sul blog di codifica di Jon Skeet.

Domanda: Posso utilizzare l'API moderna con la mia versione Java?

Se si utilizza almeno Java , è possibile.

  • In Java 8 e versioni successive, la nuova API è integrata.
  • In Java 6 e 7 ottengono the ThreeTen Backport, il backport delle nuove classi (ovvero ThreeTen per JSR-310, in cui la moderna API è stata definita per la prima volta).
  • Su Android, utilizzare l'edizione Android di ThreeTen Backport. Si chiama ThreeTenABP, e penso che ci sia una spiegazione meravigliosa in this question: How to use ThreeTenABP in Android Project.
Problemi correlati