25

w celu wykazania powtarzalnego scenariusz, robie następująceJava Konwersja GMT/UTC na czas lokalny nie działa zgodnie z oczekiwaniami

  1. Pobierz aktualny czas systemowy (czasu lokalnego)

  2. Konwersja czasu lokalnego do UTC // Działa do momentu

  3. Odwróć czas UTC, z powrotem do czasu lokalnego. Zastosowano 3 różne podejścia (wymienione poniżej), ale wszystkie 3 podejścia zachowują czas tylko w 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()); 
    

    }

Odpowiedz

51

Polecam również przy użyciu Joda jak wspomniano wcześniej.

Rozwiązywanie problemu przy użyciu standardowych JavaDate obiekty tylko można zrobić w następujący sposób:

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

wyjściowa:

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

Dobra odpowiedź! Jednak zamiast TimeZone.getDefault(). GetOffset (localTime.getTime()), powinny być TimeZone.getDefault(). GetOffset (gmtTime.getTimeInMillis()). W przeciwnym razie mogą wystąpić problemy z DaylightSaving, jeśli twój czas UTC jest zimą, ale czas lokalny jest w lecie. – Dime

+3

'nowa data (ciąg znaków)' przestarzała :( –

+0

jesteś ratownikiem T_T to jedyny sposób na rozwiązanie konfliktu java.util.date (@temporal) i znacznika czasu (postgresql) GMT vs UTC – Sarief

1

Zdecydowanie zalecamy użycie Joda Czas http://joda-time.sourceforge.net/faq.html

+0

Czy możesz podać przykład osiągnięcia tego samego przy użyciu #joda? –

+0

Nie mam nic przeciwko używaniu jody, ale chciałbym zrozumieć, dlaczego kod nie działa zgodnie z oczekiwaniami. –

5

Zwykle uważamy, że zła forma na StackOverflow.com odpowiedzieć na konkretne pytanie sugerując zastępcę technologia. Ale w przypadku klasy daty, czasu i kalendarza, dołączonej do Java 7 i wcześniejszych, klasy te są tak bardzo złe zarówno pod względem projektowania, jak i wykonywania, co zmuszało mnie do sugerowania używania biblioteki innej firmy: Joda-Time.

Joda-Time działa, tworząc immutable objects. Dlatego zamiast zmieniać strefę czasową obiektu DateTime, po prostu tworzymy nowy DateTime z przypisaną inną strefą czasową.

Główna troska o używanie czasu lokalnego i czasu UTC jest tak prosta w Joda-Time, biorąc tylko 3 linie kodu.

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

Wyjście po uruchomieniu na zachodnim wybrzeżu Ameryki Północnej może być:

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

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

Tutaj jest klasa z kilkoma przykładami i dalszych uwag. Korzystanie z 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

Masz randkę z znanej strefy czasowej (tu Europe/Madrid) i docelowa strefa czasowa (UTC)

Potrzebujesz tylko dwóch 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()); 

Więc po to widzę pracy można dodać tę metodę do 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

jestem łączenie chór zalecający pominąć już dawno przestarzałych klas Date, Calendar, SimpleDateFormat i przyjaciele. W szczególności ostrzegam przed użyciem przestarzałych metod i konstruktorów klasy Date, takich jak użyty konstruktor Date(String). Były przestarzałe, ponieważ nie działały niezawodnie w różnych strefach czasowych, więc nie należy ich używać. I tak, większość konstruktorów i metod tej klasy jest przestarzała.

W czasie, gdy zadawałeś pytanie, Joda-Time był (z tego, co wiem) zdecydowanie lepszą alternatywą, czas znów się zmienił. Dzisiaj Joda-Time to w dużej mierze ukończony projekt, a jego twórcy zalecają zamiast tego używać java.time, nowoczesnego interfejsu API daty i godziny Java. Pokażę ci jak.

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

Na początek, trzeba pamiętać, że nie tylko jest kod tylko w połowie tak długo, jak twoje, to jest także jaśniejsze czytać.

na moim komputerze drukuje kod:

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] 

Wyszedłem na zewnątrz milisekund od epoki. Zawsze możesz uzyskać je od System.currentTimeMillis();, tak jak w pytaniu, i są one niezależne od strefy czasowej, więc nie znalazłem tutaj interstingu.

Z wahaniem trzymałem zmienną nazwę localTime. Myślę, że to dobre imię. Współczesny interfejs API ma klasę o nazwie LocalTime, więc używanie tej nazwy nie jest pisane wielką literą, ponieważ obiekt, który nie ma typu LocalTime może zmylić niektóre (a LocalTime nie zawiera informacji o strefie czasowej, które musimy zachować tutaj jest w stanie dokonać prawidłowej konwersji, a także zawiera tylko porę dnia, a nie datę).

konwersji od czasu lokalnego UTC było błędne i niemożliwe

Przestarzała Date klasa nie posiada żadnych informacji strefy czasowej (można powiedzieć, że zawsze używa wewnętrznie UTC), więc nie ma takiego coś jak konwersja Date z jednej strefy czasowej na drugą. Kiedy tylko dostał swój kod na moim komputerze, to w pierwszej linii drukowanych, było:

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 jest poprawny, oczywiście.Prawidłowy czas UTC to 05:25:45 UTC, ale ponownie jest to CEST, co jest nieprawidłowe.

Teraz już nigdy nie będziesz potrzebować klasy Date :-), ale jeśli kiedykolwiek miałeś zamiar, musi przeczytać All about java.util.Date na blogu kodującego Jona Skeeta.

Pytanie: Czy mogę używać nowoczesnego API z moją wersją Java?

Jeśli używasz co najmniej Java , możesz.

  • W języku Java 8 i nowszych wersjach wbudowany jest nowy interfejs API.
  • W języku Java 6 i 7 otrzymuje the ThreeTen Backport, protokół z nowymi klasami (to ThreeTen dla JSR-310, w którym po raz pierwszy zdefiniowano nowoczesny interfejs API).
  • W systemie Android, użyj edycji Android ThreeTen Backport. Nazywa się ThreeTenABP i myślę, że jest wspaniałe wyjaśnienie w this question: How to use ThreeTenABP in Android Project.