如果我在秋日亮时结束时(在丹麦的CET时间为10-26:00)抽出时间,减去一个小时(所以我希望回到CEST 02:00 ),然后将分钟设置为零,我会得到一些奇怪的结果:
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?!)这是个虫子吗?我知道Java有一些奇怪的约定,但我看不出这怎么可能是正确的。
使用Calendar类减去一个小时并将分钟设置为0的正确方法是什么?
发布于 2014-03-24 15:09:37
以下是Java的对类似的"bug“的评论:
在“后退”期间,Calendar不支持消歧,给定的本地时间被解释为标准时间。 若要避免意外的DST更改到标准时间,请调用add()来重置值。
这意味着您应该用
cal.add(Calendar.MINUTE, -cal.get(Calendar.MINUTE));发布于 2014-03-24 15:08:52
阿潘金反应中的链接解释了问题并提出了解决方案。我确实试着用一种艰难的方式去看代码。
如果我们在拍摄前后检查DST_OFFSET:
System.out.println(cal.get(Calendar.DST_OFFSET));
cal.set(Calendar.MINUTE, 0);
System.out.println(cal.get(Calendar.DST_OFFSET));它打印:
3600000
0查看methodgetTimeInMillis,我们看到它们使用一个标志(isTimeSet)来检查在millis中重新计算时间的时间:
public long getTimeInMillis() {
if (!isTimeSet) {
updateTime();
}
return time;
}isTimeSet标志在调用set时重置为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();
}
}因此,设置重置DST偏移量。这条线获得偏移:
zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);设置值的有点下垂:
internalSet(DST_OFFSET, zoneOffsets[1]); 下一次调用getTimeInMillis时,millis的时间发生了变化。
作为阿潘金,我们不应该在初始化日期之后使用set,否则我们会强制进行某种日期重置。在OpenJDK的bug跟踪器中,这是如何建议的。
cal.add(Calendar.MINUTE, -cal.get(Calendar.MINUTE));更好的选择是使用Joda时间。这个库的作者一直在开发新的Java 8的日期时间api,我希望没有这样的问题。
发布于 2014-03-24 14:39:34
Calendar.add方法的作用必须如下:
根据日历的规则向给定的日历字段添加或减去指定的时间。
所以我们面对着神秘的日历规则。Calendar.getInstance(TimeZone.getTimeZone("CET"))提供的日历类型是什么?
执行
System.out.println(Calendar.getInstance(TimeZone.getTimeZone("CET")).getClass());我有答案:class java.util.GregorianCalendar。让我们尝试找到GregorianCalendar的规则。我在add method找到了它们(为什么在这里?)其中一条规则适合你的情况。
添加规则2.如果一个较小的字段被认为是不变的,但是它不可能等于它的先验值,因为它的最小值或最大值在字段改变后发生了变化,那么它的值就会被调整,以尽可能接近它的期望值。
01:00:00和23:00:00有相同的距离,所以根据这些规则两者都是有效的。
另外,请注意GregorianCalendar.add规范中的下一行
添加指定的(签名)时间量
它说调用cal.add(Calendar.HOUR_OF_DAY, -1);是错误的,因此没有人会责怪add只显示下一次set调用的结果。但不知何故起作用了,你可以用它!
在这里做什么?如果你认为2014-26:02:00 CET减去1小时等于它本身,那么你可以在你的set之后加上愚蠢的add。就像这样:
cal.add(Calendar.HOUR_OF_DAY, -1);
cal.set(Calendar.HOUR, cal.get(Calendar.HOUR)); //apply changes
cal.set(Calendar.MINUTE, 0);如果您希望看到-1 hour操作后的差异,那么我建议您更改TimeZone。TimeZone.getTimeZone("GMT") (例如)没有夏令时偏移量。
https://stackoverflow.com/questions/22527872
复制相似问题