这是时区变化.不是在12月31日在上海,而是在您的代码中手动进行.
特别是,您在强制日历计算其字段(基于“旧”时区)之后更改了时区.这弄乱了日历的内部状态.当然,事实并非如此,而是Calendar类所暴露的许多奇怪行为之一,而且很可能主要是由它们的可变性引起的.
在Calendar#setTimeZone的实现中的注释中还指出了一些潜在的困难:
* 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的源代码并尝试避免关键的调用顺序来解决此问题.但正如其他人已经指出的那样:整个旧的Date / Time 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;
}
}
}