我发现JAVA日历类有一个奇怪的行为。当我将分钟设置为0时,根据所使用的时区,结果会有所不同。
有人知道为什么吗?
示例代码import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class CalendarTest {
public static void main(String[] args) {
Date date = new Timestamp(1477780200000L);
System.out.println(date); // 2016-10-30 00:30:00.0
testMethod(date, Calendar.getInstance(TimeZone.getTimeZone("CET"))); // Sun Oct 30 02:00:00 CET 2016
testMethod(date, Calendar.getInstance(TimeZone.getTimeZone("GMT+1"))); // Sun Oct 30 02:00:00 CEST 2016
}
private static void testMethod(Date date, Calendar c) {
c.setTime(date);
c.add(Calendar.HOUR_OF_DAY, 2);
c.set(Calendar.MINUTE, 0);
System.out.println(c.getTime());
}
}
编辑(使我的问题更容易理解):
改变testMethod
Sun Oct 30 02:30:00 CEST 2016.
private static void testMethod(Date date, Calendar c) {
c.setTime(date);
c.add(Calendar.HOUR_OF_DAY, 2);
System.out.println(c.getTime());
}
您可以在下面的示例代码中看到,显示的日期与实时(以毫秒为单位)非常不同。问题是Sun Oct 30 02:00:00 CET
2016同时存在于CET
和CEST
时区。当切换CEST
=> CET
时,您将返回一个小时(产生小时重叠)。对于Calendar
对象,这是非常棘手的,因为您要在所选时区中表示时间变化。当您想在这个非常特定的时区重置分钟时,Calendar
必须发现您在哪个时区表示更改。
我不是100% Calendar
是如何处理这个,但在第一个例子CET
中,你特别建议你要使用CET
。因此,当重叠发生时,Calendar
可以选择您的建议(CET
)。当您使用GMT
来表示它时,Calendar
必须选择CET
或CEST
时区之一,并选择CEST
。
public static void main(String[] args) {
Date date = new Timestamp(1477780200000L);
System.out.println(date); // 2016-10-30 00:30:00.0
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("CET"));
calendar.setTime(date);
print(1, calendar); // 1: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
calendar.add(Calendar.HOUR_OF_DAY, 2);
print(2, calendar); // 2: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
calendar.set(Calendar.MINUTE, 0); // minutes expressed in CET
print(3, calendar); // 3: Sun Oct 30 02:00:00 CET 2016 | 1477789200000
System.out.println("--");
calendar = Calendar.getInstance(TimeZone.getTimeZone("CET"));
calendar.setTime(date);
print(4, calendar); // 4: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
calendar.add(Calendar.HOUR_OF_DAY, 2);
print(5, calendar); // 5: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
calendar.set(Calendar.MINUTE, 0); // minutes expressed in CET
print(6, calendar); // 6: Sun Oct 30 02:00:00 CET 2016 | 1477789200000
System.out.println("--");
calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+1"));
calendar.setTime(date);
print(4, calendar); // 4: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
calendar.add(Calendar.HOUR_OF_DAY, 2);
print(5, calendar); // 5: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
calendar.set(Calendar.MINUTE, 0); // minutes expressed in GMT
print(6, calendar); // 6: Sun Oct 30 02:00:00 CEST 2016 | 1477785600000
}
private static void print(int prefix, Calendar calendar) {
System.out.println(prefix + ": " + calendar.getTime() + " | " + calendar.getTimeInMillis());
}
更新更有趣的是Calendar
方法getTimeInMillis
:
public long getTimeInMillis() {
if (!isTimeSet) {
updateTime();
}
return time;
}
正如你所看到的,时间是更新的,当你得到它!每次使用set
方法(如calendar.set(Calendar.MINUTE, 0)
)时,有一个标志isTimeSet
被设置为false
。这意味着您的时间在正确的时区/纪元等方面变得无效。这个方法只是将给定的日历字段设置为给定的值,仅此而已。此外,如果您的设置有效,此方法不会进行任何额外的检查。另一方面,add
方法尊重日历规则并优雅地移动日期。
总结。您将分钟设置为0,这将强制日历重新计算日期。你是对的,set
是有问题的。