2013-04-23 37 views
4

最近我一直在使用時區轉換,並且我對結果感到非常驚訝。基本上,我想將日期從一個時區轉換爲另一個時區。下面是代碼,轉換工作正常,但我在調試時觀察到的是,日期不轉換,除非我撥打Calendar#get(Calendar.FIELD)帶日曆的TimeZone令人困惑的結果

private static void convertTimeZone(String date, String time, TimeZone fromTimezone, TimeZone toTimeZone){ 
     Calendar cal = Calendar.getInstance(fromTimezone); 
     String[] dateSplit = null; 
     String[] timeSplit = null; 
     if(time !=null){ 
      timeSplit = time.split(":"); 
     } 
     if(date!=null){ 
      dateSplit = date.split("/"); 
     } 
     if(dateSplit !=null){ 
      cal.set(Calendar.DATE, Integer.parseInt(dateSplit[0])); 
      cal.set(Calendar.MONTH, Integer.parseInt(dateSplit[1])-1); 
      cal.set(Calendar.YEAR, Integer.parseInt(dateSplit[2])); 
     } 
     if(timeSplit !=null){ 
      cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeSplit[0])); 
      cal.set(Calendar.MINUTE, Integer.parseInt(timeSplit[1])); 

     } 
//  System.out.println("Time in " + fromTimezone.getDisplayName() + " : " + cal.get(Calendar.DATE) +"/"+ (cal.get(Calendar.MONTH)+1)+"/"+ cal.get(Calendar.YEAR) +" " + ((cal.get(Calendar.HOUR_OF_DAY)<10) ? ("0"+cal.get(Calendar.HOUR_OF_DAY)): (cal.get(Calendar.HOUR_OF_DAY))) 
//    +":" + (cal.get(Calendar.MINUTE)<10 ? "0"+cal.get(Calendar.MINUTE) : cal.get(Calendar.MINUTE))); 
     cal.setTimeZone(toTimeZone); 
     System.out.println("Time in " + toTimeZone.getDisplayName() + " : " + cal.get(Calendar.DATE) +"/"+ (cal.get(Calendar.MONTH)+1)+"/"+ cal.get(Calendar.YEAR) +" " + ((cal.get(Calendar.HOUR_OF_DAY)<10) ? ("0"+cal.get(Calendar.HOUR_OF_DAY)): (cal.get(Calendar.HOUR_OF_DAY))) 
       +":" + (cal.get(Calendar.MINUTE)<10 ? "0"+cal.get(Calendar.MINUTE) : cal.get(Calendar.MINUTE))); 
} 

public static void main(String[] args) throws ParseException { 

     convertTimeZone("23/04/2013", "23:00", TimeZone.getTimeZone("EST5EDT"), TimeZone.getTimeZone("GB")); 
    } 

預期輸出:Time in Greenwich Mean Time : 24/4/2013 04:00

輸出我得到了我係統輸出1評論:Time in Greenwich Mean Time : 23/4/2013 23:00

如果我未評論sysout1我得到預期的有效輸出。

任何幫助表示讚賞

+0

'cal.setTimeZone()'在第一個'SOUT'之後被調用,也許它與此有關? – 2013-04-23 15:36:14

+0

@MrD日期首先處於時區EST5EDT中,即在第一次系統輸出之前。我必須將此日期轉換爲GMT。因此我在第一個sysout之後設置了時區(GB)。 – PermGenError 2013-04-23 15:38:04

+0

@MrD正如我在這個問題中所說的,轉換工作fie當我un-comment第一Sysout。:) – PermGenError 2013-04-23 15:39:39

回答

3

給定日期的內部表示不計算到真正需要的,那就是直到你試圖通過這些干將訪問它。然而,解析日期的最好方法是通過SimpleDateFormat。


編輯(添加總結下面的意見,更好地闡明我的答案)。

Calendar以這種方式提高工作效率:每次調用setter時都不會重新計算常量,而是等到您調用getter。

日曆應主要用於日期計算(見add()roll()),但你用它來解析和格式化:這些任務與SimpleDateFormat的更好實現,這就是爲什麼我說,你的日曆的使用是不優雅。

見這個例子:

private static void convertTimeZone(String date, String time, 
      TimeZone fromTimezone, TimeZone toTimeZone) throws ParseException { 
    SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm"); 
    df.setTimeZone(fromTimezone); 
    Date d = df.parse(date + " " + time); 
    df.setTimeZone(toTimeZone); 
    System.out.println("Time in " + toTimeZone.getDisplayName() + " : " + 
         df.format(d)); 
} 

我只使用的SimpleDateFormat重新實現你的方法。我的方法更小,沒有分裂邏輯(它隱藏在parse()中),並且輸出也以更簡單的方式處理。此外,日期格式以緊湊和標準的方式表達,可以使用ResourceBundle輕鬆實現國際化。

還要注意的是,時區轉換隻是一個格式任務:解析日期的內部表示不變化。

+1

你能準確地描述[爲什麼不](http://grepcode.com/file/repository.grepcode的.com /爪哇/根/ JDK /的openjdk/6-B14/JAVA/util的/ Calendar.java#日曆)? – 2013-04-23 15:47:00

+0

嗯,重點是它應該給我適當的日期時間轉換後的時區。即爲什麼需要調用Calendar#get(Fields)兩次?一次之前,我轉換和一次後,我轉換? – PermGenError 2013-04-23 15:47:33

+0

爲了獲得更高的效率:每次調用setter時,不要重新計算每秒的次數,而要等到您調用getter。您使用日曆的方式並不優雅,我建議您使用SimpleDateFormat。 – Pino 2013-04-23 15:51:54

0

您是否僅限於使用TimeZone和日曆?如果沒有,我建議使用優秀的庫JodaTime,這使得處理時區變得更容易。然後

你的榜樣應該是這樣的:

public static void convertTimeZoneJoda(String date, String time, DateTimeZone fromTimezone, DateTimeZone toTimeZone) { 
    DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyyHH:mm").withZone(fromTimezone); 
    DateTime dt = dtf.parseDateTime(date+time).withZone(toTimeZone); 

    System.out.println("Time in " + toTimeZone.getID() + " : " + dt.toString()); 
} 

public static void main(String[] args) { 
    convertTimeZoneJoda("23/04/2013", "23:00", DateTimeZone.forID("EST5EDT"), DateTimeZone.forID("GMT")); 
} 

JodaTime還允許時區和DateTimeZone之間方便交談。

+0

不幸的是,我僅限於使用TimeZone和日曆。正如你可以看到它的一個簡單的時區轉換。所以基本的Calendar和TimeZOne API應該這樣做。我不知道我在哪裏做錯了,但:) – PermGenError 2013-04-23 15:48:45

1

答案在a commented section in setTimeZone()部分解釋:

考慮調用序列:cal.setTimeZone(EST); cal.set(HOUR,1); cal.setTimeZone(PST)。 是否設置爲美國東部時間1點或太平洋標準時間1點?答案:PST。更多 通常,調用setTimeZone()會影響調用set()之前和之後 直到下一次調用complete()。

換句話說,序列

  1. 設置日曆時間
  2. 更改時區

被解釋爲「在這個新的時區利用這段時間」,而序列

  1. 設置日曆t IME
  2. 獲取時間(或它的某些部分)
  3. 更改時區

將被解釋爲「利用這段時間在舊的時區,然後改變它。」

所以,你的情況,讓你希望的行爲,你將需要調用get(),或在內部調用complete()Calendar內,您更改時區前,任何其他方法。

0

爲了增加@Pino的答案,爲什麼get()不返回更新的時間的原因是因爲setTimeZone()根本不更新的領域,只是設置areAllFieldsSet爲false,這是無用的,因爲get()不檢查它。如果你問我,SUN的部分代碼很差。下面是從Calendar主管代碼:

setTimeZone()

public void setTimeZone(TimeZone value){ 
     zone = value; 
     sharedZone = false; 
     areAllFieldsSet = areFieldsSet = false; 

    } 

get()

protected final int internalGet(int field){ 
    return fields[field]; 
} 
0

此代碼對我的作品轉換爲UTC:

  1. 創建與UTC時區中的日曆對象

    Calendar utcTime = Calendar.getInstance(TimeZone.getTimeZone("GMT")); 
    
  2. 設置在UTC Calendar對象的時間點從「任何時區」日曆對象

    utcTime.setTimeInMillis(myCalendarObjectInSomeOtherTimeZone.getTimeInMillis()); 
    
  3. utcTime現在將包含與myCalendarObjectInSomeOtherTimeZone轉換爲UTC的時間點相同。