2014-10-29 110 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

相关问题