2012-11-07 23 views
2

我有一些使用Calendar.set()返回給定日期值的小時開始的代碼。我遇到了以下問題上週日2012年11月4日(美國東部時區 - 東部時間到EST切換):Java日曆:上午1時切換到標準時間(從DST開始)

public void testStartOfHourDST1() { 

    Calendar cal = Calendar.getInstance(); 

    long time = 1352005200000L; // Nov 4, 2012, 1AM EDT 
    cal.setTimeInMillis(time); 

    cal.set(Calendar.MILLISECOND, 0); 
    cal.set(Calendar.SECOND, 0); 
    cal.set(Calendar.MINUTE, 0); 

    System.out.println(new Date(time)); 
    System.out.println(new Date(cal.getTimeInMillis())); 
    System.out.println(System.getProperty("java.version")); 

    assertEquals(cal.getTimeInMillis(), time); // fails 

    return; 
} 

輸出繼電器:

太陽年11月4 01:00:00 EDT 2012

孫年11月4 01:00:00 EST 2012

1.6.0_35

Perha ps這不是使用日曆的正確方法,但在下一小時(或上一小時)運行相同的測試可以正常工作。這是一個JVM問題?

感謝

回答

5

這不是一個真正的問題,在某種意義上說,它是確定的,做什麼,這是編程的事。這是一個問題,如果你更喜歡它選擇兩個1ams中的更早的!

更改日曆中的字段後,它所擁有的唯一信息是「美國/東部時間上午1點」。那麼,你的時區當天有兩個1am,應該選哪一個呢? OpenJDK的作者做出了一個決定,即在呈現這種模糊性時,他們總是會在標準時間將其解釋爲後者。這個評論是java.util.GregorianCalendar中的OpenJDK 6:

// 2. The transition out of DST. Here, a designated time of 1:00 am - 1:59 am 
    // can be in standard or DST. Both are valid representations (the rep 
    // jumps from 1:59:59 DST to 1:00:00 Std). 
    // Again, we assume standard time. 

如果打印出來的數字的實際值,你會看到cal.getTimeInMillis()實際上是由一個小時從time值改變。

+0

好點。因此,Calendar似乎不支持消除這兩個重疊時間的方法(除了使用setTime/setTimeInMillis或通過使用@Nambari提到的setTimeZone。 – monex0

+0

日曆是使用_most最近time_還是應用默認時區在創建日曆時(如果在夏令時開關發生後程序正在運行,這將是EST)......我不知道答案 – jahroy

+0

@jahroy代碼中的註釋java.util .GregorianCalendar表示,它總是將當天的1:00-1:59解釋爲處於標準時間,而不是作爲任意約定的夏令時。「......這裏指定的時間是凌晨1點 - 凌晨1:59可以是標準時間或夏令時,兩者都是有效的表示方式(代表從標準時間1:59:59跳到1:00:00標準時間),再次假設標準時間「 – Affe

0

獲得正確的時區非常重要。例如,我確定您知道系統當前時間(以毫秒爲單位)是從1970年1月1日午夜開始測量的,這是有據可查的。 Date構造的JavaDoc說:

公共日期(長日期)

分配Date對象並對其進行初始化,以表示從被稱爲「 曆元的標準基準時間指定 的毫秒數「,即1970年1月1日,格林威治標準時間00:00:00。

這是很好的,但使這個節目有點硬的輸出首先要了解:

import java.util.Date; 
import java.util.TimeZone; 
import java.text.SimpleDateFormat; 

public class Demo { 
    public static void main(String[] args) { 
     TimeZone tz = TimeZone.getTimeZone("Europe/London"); 
     Date d = new Date(-3600000); 

     SimpleDateFormat df = new SimpleDateFormat("MMMM dd, yyyy HH:mm:ss.SSS z"); 
     df.setTimeZone(tz); 
     System.out.println(String.format("%8dms -> %s", 
        Long.valueOf(d.getTime()) ,df.format(d))); 

     d.setTime(0); 
     System.out.println(String.format("%8dms -> %s", 
        Long.valueOf(d.getTime()) ,df.format(d))); 
    } 
} 

輸出是:

-3600000ms -> January 01, 1970 00:00:00.000 GMT 
     0ms -> January 01, 1970 01:00:00.000 GMT 

正如你所看到的,劃時代顯然是流離失所了一個小時!

除外。如果明確使用「GMT」時區,一切正常。 「歐洲/倫敦」時區就像這樣簡單。

使用日曆時,請了解您正在使用的時區或被發現的時區。

+0

感謝您的信息,但這個例子有點令人困惑,因爲日期對不同時區打印「GMT」。如果你使用'Z'輸出格式,它將顯示+0100(這是清楚的)。所以你的例子中的問題不是歐洲/倫敦是'棘手',而只是字符串輸出不明確。我的錯誤是沒有意識到set()函數會導致Calendar重新從頭開始重新評估其UTC時間(因此必須弄清楚要使用哪個'1am')之間的混淆。原始問題僅在支持DST偏移的時區出現。 – monex0

相關問題