2015-07-03 62 views
7

請參見下面的代碼:SimpleDateFormat的行爲不一致

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

現在,我有+2h偏移一個國家敢,+1夏令時(目前)。 如果我運行這段代碼,因爲它是,它會打印

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

如果我取消對該行詢問該日曆的偏移,該程序的輸出是

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

是否知道這是爲什麼發生的事情,以及如何獲得一致的輸出?

爲了避免任何更多的東西不清: 由於我處理一些遺留代碼,JAVA 8是不是一種選擇。是的,這裏的關鍵點是爲什麼,而不是解決方法是什麼? 有2個爲什麼:

  1. 爲什麼我通過一個+0300 TZ,默認情況下我收到一個+0200一個? (除非另有指定,否則SimpleDateFormat應始終使用TimeZone.getDefault,除非 )。
  2. 爲什麼它給出了不同的答案,只是因爲我呼籲一個getter它的日曆實例。
+3

它有什麼不一致嗎?當您不使用偏移量時,它會按照原樣顯示日期,並在使用時根據時區進行更改。 –

+1

@RamanShrivastava調用一次getter不應該改變格式函數的輸出。 – wonderb0lt

回答

2

的問題是,Calendar#get不是一個簡單的getter,它看起來像這樣:

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

其中complete做到這一點,根據的Javadoc:

填充任何未設置日曆字段中的字段。首先,如果時間值(毫秒從曆元偏移)尚未從日曆字段值中計算出的computeTime()方法被調用。然後,調用computeFields()方法計算所有日曆字段值。

我擡頭看看源碼here,但代碼也是最新的Java版本。

+0

是的,我知道。但這只是「這是因爲它做到了」。對我來說,它絕對看起來像一個錯誤。 –

+0

我會說,這是一個有爭議的設計決定。有一個原因,爲什麼Java 8有一個新的日期和時間API :) – meskobalazs

+0

這個答案將是完美的一個小片段如何OP可以改變他的代碼,以避免'complete()'打破他的代碼(例如設置一些特定的領域日曆實例等) – wonderb0lt

0

sdf.parse改變格式化內部日曆區偏移量+0300

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

你可以在輸出線末端看到了差距

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

sdf.getCalendar().get(Calendar.ZONE_OFFSET);恢復日曆的區偏移回到當前時區

+1

問題是:爲什麼要這樣做?我的預感如下:日曆有一個時區對象的引用。解析日期時,不能從「+0300」確定時區。它只是在日曆的'ZONE_OFFSET'字段中存儲3小時的偏移量。 'get()'方法在內部計算日曆的所有字段,但尚未對所有字段執行此操作,包括「ZONE_OFFSET」字段。日曆使用它知道的時區來計算偏移量並確定不同的偏移量。 – gogognome

+0

是的,我假設這是最準確的答案。實際上,我現在的位置,正如我在最初的帖子中所寫的,我在+2偏移量+1 dst區域中。這在SimpleDateFormat模式中不能以任何方式進行表示。這就是爲什麼我們將它合併爲+3。但是,仍然存在Calendar和SimpleDateFormat在整個內部處理過程中存在的錯誤,因爲在(+2 +1)中轉換(+3 +0)或保持原樣的行爲應該一致,並且不會受到影響通過獲得。由於我沒有在創建後以任何方式修改sdf,因此sdf.format(sdf.parse(txt))應始終爲txt。 –

0

SimpleDateFormate使用日曆創建日期。日期對象是使用時間戳創建的,計算結果見java.util.GregorianCalendar.computeTime()。在2789行中使用了初始時區(其設置了java.text.SimpleDateFormat.initializeCalendar(Locale))。這就是爲什麼你看到錯誤的時區。

當你調用get(Calendar.ZONE_OFFSET)方法java.util.GregorianCalendar.computeFields()被調用,它使用最初設定時區。

我猜這是一個錯誤。