将 Java 日历对象设置为午夜的行为不一致
Inconsistent behavior with setting Java calendar object to midnight
我有一个 Java 程序,我需要将 Calendar 对象设置为午夜,所以我按照 how to create a Java Date object of midnight today and midnight tomorrow 上另一篇帖子的说明进行操作,其中您将字段设置为 0,像这样:
calStart.set(Calendar.HOUR_OF_DAY, 0);
calStart.set(Calendar.MINUTE, 0);
calStart.set(Calendar.SECOND, 0);
calStart.set(Calendar.MILLISECOND, 0);
但是,我使用 Java 1.7.0_51 得到的结果不一致。 (请不要提供有关使用 Jodatime 的建议,因为我只想使用基础库。)
这是我的测试程序:
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
public class MidnightTester {
public static void runTestMidnight1() {
System.out.printf("runTestMidnight1() called\n");
TimeZone tz = TimeZone.getTimeZone("UTC");
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(1437004800000L);
cal.setTimeZone(tz);
System.out.printf("Original timestamp:\n");
printCalendarWithTimeZone(cal, tz);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
System.out.printf("Timestamp after setting to midnight:\n");
printCalendarWithTimeZone(cal, tz);
}
public static void runTestMidnight2() {
System.out.printf("runTestMidnight2() called\n");
TimeZone tz = TimeZone.getTimeZone("UTC");
Calendar cal = Calendar.getInstance();
cal.setTimeZone(tz);
cal.setTimeInMillis(1437004800000L);
System.out.printf("Original timestamp:\n");
printCalendarWithTimeZone(cal, tz);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
System.out.printf("Timestamp after setting to midnight:\n");
printCalendarWithTimeZone(cal, tz);
}
public static void printCalendarWithTimeZone(Calendar cal, TimeZone tz) {
SimpleDateFormat sdf = new SimpleDateFormat("EEEE, MMMM dd, yyyy, hh:mm:ss aaa");
sdf.setTimeZone(tz);
System.out.printf("%d= %s\n", cal.getTimeInMillis(), sdf.format(cal.getTime()));
}
public static void main(String argv[]) {
runTestMidnight1();
runTestMidnight2();
}
}
这是输出:
runTestMidnight1() called
Original timestamp:
1437004800000= Thursday, July 16, 2015, 12:00:00 AM
Timestamp after setting to midnight:
1436918400000= Wednesday, July 15, 2015, 12:00:00 AM
runTestMidnight2() called
Original timestamp:
1437004800000= Thursday, July 16, 2015, 12:00:00 AM
Timestamp after setting to midnight:
1437004800000= Thursday, July 16, 2015, 12:00:00 AM
如您所见,在runTestMidnight1()中,将时间设置为午夜后,日期从7月16日更改为7月15日!我不明白。
更奇怪的是,在 runTestMidnight2() 中,我只交换了两行,并且日期正确地保持在 7 月 16 日不变。
cal.setTimeZone(tz);
cal.setTimeInMillis(1437004800000L);
我在 America/Pacific 时间,以防万一。
有人可以帮助我了解发生了什么吗?
编辑:更好的解释
当我们调用 setTimeInMillis() 时,传递的 long 值被假定为 UTC。并且它使用用户的时区或日历对象是否已经可用的任何时区来计算日历对象时间中的时间。
如果没有传递时区,则在日历对象上设置用户的时区。因此,值“1437004800000L”被认为是在 PST 时区,时间是用 PST 时区计算的。
在以毫秒为单位设置时间后调用 setTimeZone() 会更改 Calendar 对象中的时区。所以日历对象时间被移动到 UTC。 runTestMidnight1() 就是这种情况。这就是我们看到日期发生变化的原因,因为原始时间是太平洋标准时间。
而在第二种方法中,时区首先由 setTimeZone() 设置为 UTC。因此,时间值为“1437004800000L”,时区为 UTC,计算时间为 UTC。而且您只获得 UTC 时间。
只有当用户所在的时区落后于 UTC 时区时,此问题才会重现。
希望它澄清。
我有一个 Java 程序,我需要将 Calendar 对象设置为午夜,所以我按照 how to create a Java Date object of midnight today and midnight tomorrow 上另一篇帖子的说明进行操作,其中您将字段设置为 0,像这样:
calStart.set(Calendar.HOUR_OF_DAY, 0);
calStart.set(Calendar.MINUTE, 0);
calStart.set(Calendar.SECOND, 0);
calStart.set(Calendar.MILLISECOND, 0);
但是,我使用 Java 1.7.0_51 得到的结果不一致。 (请不要提供有关使用 Jodatime 的建议,因为我只想使用基础库。)
这是我的测试程序:
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
public class MidnightTester {
public static void runTestMidnight1() {
System.out.printf("runTestMidnight1() called\n");
TimeZone tz = TimeZone.getTimeZone("UTC");
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(1437004800000L);
cal.setTimeZone(tz);
System.out.printf("Original timestamp:\n");
printCalendarWithTimeZone(cal, tz);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
System.out.printf("Timestamp after setting to midnight:\n");
printCalendarWithTimeZone(cal, tz);
}
public static void runTestMidnight2() {
System.out.printf("runTestMidnight2() called\n");
TimeZone tz = TimeZone.getTimeZone("UTC");
Calendar cal = Calendar.getInstance();
cal.setTimeZone(tz);
cal.setTimeInMillis(1437004800000L);
System.out.printf("Original timestamp:\n");
printCalendarWithTimeZone(cal, tz);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
System.out.printf("Timestamp after setting to midnight:\n");
printCalendarWithTimeZone(cal, tz);
}
public static void printCalendarWithTimeZone(Calendar cal, TimeZone tz) {
SimpleDateFormat sdf = new SimpleDateFormat("EEEE, MMMM dd, yyyy, hh:mm:ss aaa");
sdf.setTimeZone(tz);
System.out.printf("%d= %s\n", cal.getTimeInMillis(), sdf.format(cal.getTime()));
}
public static void main(String argv[]) {
runTestMidnight1();
runTestMidnight2();
}
}
这是输出:
runTestMidnight1() called
Original timestamp:
1437004800000= Thursday, July 16, 2015, 12:00:00 AM
Timestamp after setting to midnight:
1436918400000= Wednesday, July 15, 2015, 12:00:00 AM
runTestMidnight2() called
Original timestamp:
1437004800000= Thursday, July 16, 2015, 12:00:00 AM
Timestamp after setting to midnight:
1437004800000= Thursday, July 16, 2015, 12:00:00 AM
如您所见,在runTestMidnight1()中,将时间设置为午夜后,日期从7月16日更改为7月15日!我不明白。
更奇怪的是,在 runTestMidnight2() 中,我只交换了两行,并且日期正确地保持在 7 月 16 日不变。
cal.setTimeZone(tz);
cal.setTimeInMillis(1437004800000L);
我在 America/Pacific 时间,以防万一。
有人可以帮助我了解发生了什么吗?
编辑:更好的解释
当我们调用 setTimeInMillis() 时,传递的 long 值被假定为 UTC。并且它使用用户的时区或日历对象是否已经可用的任何时区来计算日历对象时间中的时间。
如果没有传递时区,则在日历对象上设置用户的时区。因此,值“1437004800000L”被认为是在 PST 时区,时间是用 PST 时区计算的。 在以毫秒为单位设置时间后调用 setTimeZone() 会更改 Calendar 对象中的时区。所以日历对象时间被移动到 UTC。 runTestMidnight1() 就是这种情况。这就是我们看到日期发生变化的原因,因为原始时间是太平洋标准时间。
而在第二种方法中,时区首先由 setTimeZone() 设置为 UTC。因此,时间值为“1437004800000L”,时区为 UTC,计算时间为 UTC。而且您只获得 UTC 时间。
只有当用户所在的时区落后于 UTC 时区时,此问题才会重现。
希望它澄清。