2014-10-29 101 views
3

我發現GregorianCalendar.getTimeInMillis()的一個非常奇怪的行爲,它似乎改變了實例內容的值。在下面的代碼中,您可以看到兩個代碼塊僅在一個註釋行中有所不同,其中getTimeInMillis()被調用。爲什麼我取消註釋時結果不同?爲什麼GregorianCalendar.getTimeInMillis()更改實例的值?

隨着評論調用輸出是

2014-10-25T22:00:00Z -> 2014-10-26T22:00:00.000+01:00 
2014-10-25T22:00:00Z -> 2014-10-27T00:00:00.000+01:00 

但是當我取消對getTimeInMillis()線,兩個結果都是相同的:

2014-10-25T22:00:00Z -> 2014-10-27T00:00:00.000+01:00 
2014-10-25T22:00:00Z -> 2014-10-27T00:00:00.000+01:00 

代碼:

package com.test; 

import java.util.Calendar; 
import java.util.GregorianCalendar; 
import java.util.TimeZone; 

import javax.xml.datatype.DatatypeFactory; 
import javax.xml.datatype.XMLGregorianCalendar; 

public class Main { 

public static void main(String[] args) { 
    try { 
     XMLGregorianCalendar date1 = DatatypeFactory.newInstance() 
       .newXMLGregorianCalendar("2014-10-25T22:00:00Z"); 
     XMLGregorianCalendar date2 = DatatypeFactory.newInstance() 
       .newXMLGregorianCalendar("2014-10-25T22:00:00Z"); 
     int days = 1; 

     GregorianCalendar gregorianCalendar1 = date1.toGregorianCalendar(); 
     // gregorianCalendar1.getTimeInMillis(); //UNCOMMENT THIS LINE TO GET A DIFFERENT RESULT 
     gregorianCalendar1.setTimeZone(TimeZone.getDefault()); 
     gregorianCalendar1.add(Calendar.DAY_OF_MONTH, days); 
     XMLGregorianCalendar newXMLGregorianCalendar1 = DatatypeFactory 
       .newInstance().newXMLGregorianCalendar(gregorianCalendar1); 
     System.out.printf("%s -> %s\n", date1, newXMLGregorianCalendar1); 

     GregorianCalendar gregorianCalendar2 = date2.toGregorianCalendar(); 
     gregorianCalendar2.getTimeInMillis(); 
     gregorianCalendar2.setTimeZone(TimeZone.getDefault()); 
     gregorianCalendar2.add(Calendar.DAY_OF_MONTH, days); 
     XMLGregorianCalendar newXMLGregorianCalendar2 = DatatypeFactory 
       .newInstance().newXMLGregorianCalendar(gregorianCalendar2); 
     System.out.printf("%s -> %s\n", date2, newXMLGregorianCalendar2); 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

} 
+0

此錯誤報告中也描述了此行爲:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=5026826 – Marco13 2014-10-30 09:43:22

回答

2

這是一個時區變化。不是在12月31日在上海,而是在代碼中手動。

特別是,您正在更改後的時區強制日曆計算其字段(基於「舊」時區)。這擾亂了日曆的內部狀態。當然,情況並非如此,但僅僅是Calendar類別暴露的許多奇怪行爲之一 - 並且很可能主要是由它們的可變性引起的。

* Consider the sequence of calls: 
* cal.setTimeZone(EST); cal.set(HOUR, 1); cal.setTimeZone(PST). 
* Is cal set to 1 o'clock EST or 1 o'clock PST? Answer: PST. 

你可能解決此通過研究GregorianCalendar的源代碼,並試圖避免調用的關鍵序列:

一些潛在的困難也在在Calendar#setTimeZone落實評論說。但正如其他人已經指出的:整個舊的日期/時間API是可怕的破碎。如果有機會,您應該考慮使用new Date/Time API of Java 8(或Joda Time API,它與Java 8相似,以便稍後將現有的基於Joda的代碼更改爲Java 8代碼)。


這裏是一個演示了getTimeMillis調用getTimeMillis呼叫設定前時間段內之間的區別的例子:

import java.util.Calendar; 
import java.util.GregorianCalendar; 
import java.util.TimeZone; 

import javax.xml.datatype.DatatypeFactory; 
import javax.xml.datatype.XMLGregorianCalendar; 

public class GregorianCalendarTest { 

    public static void main(String[] args) { 
     String fromSettingTimeZoneBeforeCall = createString(true); 
     String fromSettingTimeZoneAfterCall = createString(false); 

     System.out.println("Before: "+fromSettingTimeZoneBeforeCall); 
     System.out.println("After : "+fromSettingTimeZoneAfterCall); 
    } 

    private static String createString(boolean setTimeZoneBeforeCall) 
    { 
     try { 
      XMLGregorianCalendar date = DatatypeFactory.newInstance() 
       .newXMLGregorianCalendar("2014-10-25T22:00:00Z"); 
      int days = 1; 

      GregorianCalendar gregorianCalendar = date.toGregorianCalendar(); 
      System.out.println("After creating: "+gregorianCalendar); 

      if (!setTimeZoneBeforeCall) 
      { 
       gregorianCalendar.getTimeInMillis(); 
       System.out.println("After millis : "+gregorianCalendar); 
      } 

      gregorianCalendar.setTimeZone(TimeZone.getDefault()); 
      System.out.println("After timezone: "+gregorianCalendar); 

      if (setTimeZoneBeforeCall) 
      { 
       gregorianCalendar.getTimeInMillis(); 
       System.out.println("After millis : "+gregorianCalendar); 
      } 

      gregorianCalendar.add(Calendar.DAY_OF_MONTH, days); 
      System.out.println("After adding : "+gregorianCalendar); 

      XMLGregorianCalendar newXMLGregorianCalendar = DatatypeFactory 
       .newInstance().newXMLGregorianCalendar(gregorianCalendar); 
      System.out.println("After all  : "+gregorianCalendar); 

      return newXMLGregorianCalendar.toString(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 
} 

編輯:此行爲在這個錯誤報告中也有描述:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=5026826

2

預-Java 8日曆實現在「怪異」行爲下受到很多批評。我認爲,這是由於下列文件:

Getting and Setting Calendar Field Values The calendar field values can be set by calling the set methods. Any field values set in a Calendar will not be interpreted until it needs to calculate its time value (milliseconds from the Epoch) or values of the calendar fields. Calling the get, getTimeInMillis, getTime, add and roll involves such calculation.

注意,toString()方法被標記爲只調試:

Return a string representation of this calendar. This method is intended to be used only for debugging purposes, and the format of the returned string may vary between implementations. The returned string may be empty but may not be null.

雖然這不會最終可能(只要你沒有在實際邏輯中使用toString()),最好使用Joda-Time或新的Java-8 Date and Time

相關問題