Ras*_*ber 14 java calendar gregorian-calendar dst
如果我在秋季结束时点亮时间(2014-10-26 02:00:00 CET in Denmark)并减去一小时(所以我希望回到02:00 CEST)然后将分钟设置为零,我得到一些奇怪的结果:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("CET"));
cal.set(Calendar.YEAR, 2014);
cal.set(Calendar.MONTH, Calendar.OCTOBER);
cal.set(Calendar.DAY_OF_MONTH, 26);
cal.set(Calendar.HOUR_OF_DAY, 2);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
System.out.println(cal.getTimeInMillis()); // 1414285200000 : 01:00:00 UTC
cal.add(Calendar.HOUR_OF_DAY, -1);
System.out.println(cal.getTimeInMillis()); // 1414281600000 : 00:00:00 UTC (as expected)
cal.set(Calendar.MINUTE, 0);
// or: cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE));
// both should be NOPs.
System.out.println(cal.getTimeInMillis()); // 1414285200000 : 01:00:00 UTC (Why?!)
Run Code Online (Sandbox Code Playgroud)
这是一个错误吗?我知道Java Calendar有一些奇怪的约定,但我看不出这是如何正确的.
使用Calendar类减去一小时并将分钟设置为0的正确方法是什么?
apa*_*gin 11
在"后退"期间,日历不支持消歧,并且给定的本地时间被解释为标准时间.
要避免意外的DST到标准时间更改,请调用add()以重置值.
这意味着你应该更换set()使用
cal.add(Calendar.MINUTE, -cal.get(Calendar.MINUTE));
Run Code Online (Sandbox Code Playgroud)
apangin的回复中的链接说明了问题并提出了解决方案。我确实尝试用困难的方式查看代码。
如果我们在设置之前和之后检查DST_OFFSET:
System.out.println(cal.get(Calendar.DST_OFFSET));
cal.set(Calendar.MINUTE, 0);
System.out.println(cal.get(Calendar.DST_OFFSET));
Run Code Online (Sandbox Code Playgroud)
它打印:
3600000
0
Run Code Online (Sandbox Code Playgroud)
查看methodgetTimeInMillis方法,我们看到它们在重新计算以毫秒为单位的时间时使用标志(isTimeSet)进行检查:
public long getTimeInMillis() {
if (!isTimeSet) {
updateTime();
}
return time;
}
Run Code Online (Sandbox Code Playgroud)
调用set时,标志isTimeSet被重置为false 。从GregorianCalendar的来源:
public void set(int field, int value)
{
if (isLenient() && areFieldsSet && !areAllFieldsSet) {
computeFields();
}
internalSet(field, value);
isTimeSet = false; // <-------------- here
areFieldsSet = false;
isSet[field] = true;
stamp[field] = nextStamp++;
if (nextStamp == Integer.MAX_VALUE) {
adjustStamp();
}
}
Run Code Online (Sandbox Code Playgroud)
因此设置重置DST偏移量。这行获取偏移量:
zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
Run Code Online (Sandbox Code Playgroud)
一点点下来的值被置:
internalSet(DST_OFFSET, zoneOffsets[1]);
Run Code Online (Sandbox Code Playgroud)
下次调用getTimeInMillis时,以毫秒为单位的时间将更改。
正如apangin建议的那样,我们不应该在初始化日期之后使用set,否则我们将强制执行某种日期重置。这就是OpenJDK错误跟踪程序中的建议。
cal.add(Calendar.MINUTE, -cal.get(Calendar.MINUTE));
Run Code Online (Sandbox Code Playgroud)
更好的选择是使用Joda Time。该库的作者一直在研究新的Java 8的日期时间api,我希望不会出现此类问题。