2015-07-03 34 views
7

Zobacz następujący fragment kodu:SimpleDateFormat zachowuje się niekonsekwentnie

String timeString = "1980-01-01T14:00:00+0300"; 
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); 
Date date2 = sdf.parse(timeString); 

// sdf.getCalendar().get(Calendar.ZONE_OFFSET); 
System.out.println(sdf.format(date2)); 

Teraz jestem w kraju, który ma +2h offsetu, +1 letniego (w tej chwili). Jeśli uruchomić ten kod, jak to jest, będzie drukować

1980-01-01T13:00:00+0200 

Gdybym odkomentuj linię prośbą o kalendarz offset, wyjście programu jest

1980-01-01T14:00:00+0300 

Każdy pomysł, dlaczego jest to, że dzieje i jak mogę uzyskać spójne wyniki?

Aby uniknąć bardziej niejasnych rzeczy: Ponieważ zajmuję się jakimś starszym kodem, java 8 nie jest opcją. I tak, kluczową kwestią jest DLACZEGO, a nie jakie są rozwiązania? i istnieją 2 why:

  1. Dlaczego zdać +0300 TZ i domyślnie otrzymam +0200 jeden? (SimpleDateFormat powinien zawsze używać TimeZone.getDefault, chyba że podano inaczej).
  2. Dlaczego daje inną odpowiedź tylko dlatego, że wywołuję gettera w jego instancji kalendarza.
+3

co jest niespójne w tym? kiedy nie używasz przesunięcia, daje on datę taką, jaka jest, a kiedy jej używasz zmienia się zgodnie ze strefą czasową. –

+1

@RamanShrivastava Wywołanie gettera nie powinno zmieniać wyjścia funkcji formatu. – wonderb0lt

Odpowiedz

2

Problem polega na tym, że Calendar#get nie jest proste, przebojowa, wygląda to tak:

public int get(int field) 
{ 
    complete(); 
    return internalGet(field); 
} 

gdzie complete robi to zgodnie z Javadoc:

wypełnia każdy wyłączony pola w polach kalendarza. Najpierw wywoływana jest metoda computeTime(), jeśli wartość czasu (przesunięcie milisekundy od Epoki) nie została obliczona z wartości pól kalendarza. Następnie wywoływana jest metoda computeFields() w celu obliczenia wszystkich wartości pól kalendarza.

Sprawdziłem źródło here, ale kod jest taki sam dla najnowszej wersji Java.

+0

Tak, wiedziałem o tym. Ale to po prostu "robi to, ponieważ to robi". Dla mnie zdecydowanie wygląda jak błąd. –

+0

Powiedziałbym, że jest to dyskusyjna decyzja projektowa. Jest powód, dla którego Java 8 ma nowy termin i czas API :) – meskobalazs

+0

Ta odpowiedź byłaby idealna z małym urywkiem, w jaki sposób OP mógłby zmienić swój kod, aby uniknąć, że 'complete()' łamie jego kod (np. Ustawiając pewne określone pola na instancja kalendarza itp.) – wonderb0lt

0

sdf.parse zmienia wewnętrzna strefa kalendarz formater offset do +0300

System.out.println(sdf.getCalendar()); 
    sdf.parse(timeString); 
    System.out.println(sdf.getCalendar()); 

widać różnicę na koniec linii wyjściowych

... ,ZONE_OFFSET=7200000,DST_OFFSET=0] 
... ,ZONE_OFFSET=10800000,DST_OFFSET=0] 

sdf.getCalendar().get(Calendar.ZONE_OFFSET); przywraca strefę kalendarza przesunięcia z powrotem do aktualnej strefy czasowej

+1

Pytanie brzmi: dlaczego to robi? Moje przeczucie jest następujące: Kalendarz ma odniesienie do obiektu strefy czasowej. Podczas analizowania daty nie można określić strefy czasowej z "+0300". Po prostu przechowuje przesunięcie przez 3 godziny w polu "ZONE_OFFSET" kalendarza. Metoda 'get()' wewnętrznie oblicza wszystkie pola dla kalendarza, gdy jeszcze tego nie zrobiono dla wszystkich pól, w tym pola 'ZONE_OFFSET'. Kalendarz używa strefy czasowej, którą zna, aby obliczyć przesunięcie i określa inne przesunięcie. – gogognome

+0

Tak, przypuszczam, że jest to najbardziej dokładna odpowiedź. Właściwie, gdzie teraz jestem, tak jak napisałem w pierwszym poście, jestem w strefie +2 przesunięcia, +1 dst. Tego nie można w żaden sposób reprezentować w wzorzec SimpleDateFormat. Dlatego łączymy go jako +3. Ale wtedy, gdzieś tam jest błąd w całym wewnętrznym przetwarzaniu, jaki robią Kalendarz i SimpleDateFormat, ponieważ zachowanie transformacji (+3 +0) w (+2 +1) lub pozostawienie go w takim stanie, w jakim było, powinno być spójne i nie wpływać przez get. Ponieważ w żaden sposób nie modyfikuję sdf, sdf.format (sdf.parse (txt)) powinien zawsze być txt. –

0

SimpleDateFormate używa Kalendarza do tworzenia Daty. Obiekt Date jest tworzony z datą, która jest obliczana w java.util.GregorianCalendar.computeTime(). Tam w linii 2789 używana jest początkowa strefa czasowa (która została ustawiona jako java.text.SimpleDateFormat.initializeCalendar(Locale)). To dlatego widzisz niewłaściwą strefę czasową.

Po wywołaniu get(Calendar.ZONE_OFFSET) wywoływana jest metoda java.util.GregorianCalendar.computeFields(), która korzysta z strefy czasowej, która została ustawiona początkowo.

Zgaduję, że to jest błąd.