【问题标题】:Inconsistent behavior with setting Java calendar object to midnight将 Java 日历对象设置为午夜的行为不一致
【发布时间】:2024-01-17 20:58:01
【问题描述】:

我有一个 Java 程序,我需要将日历对象设置为午夜,所以我按照 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);

我在美国/太平洋时间,以防万一。

有人可以帮我理解发生了什么吗?

【问题讨论】:

  • 无法重现您的错误;它显示Thursday, July 16, 2015, 12:00:00 AM 4 次。我正在使用“jdk1.7.0_ 25”并且在亚洲/新加坡时区。仅当我省略了对cal.setTimeZone 的第一次调用时,我会得到一些类似的东西,但第二个时间戳是 7 月 15 日下午 4 点。
  • 我使用的是 java 1.7.0_51。
  • 您能否使用-Duser.timezone=America/Los_Angeles 设置您的JVM 时区(来自*.com/questions/2627992/…)?
  • 即使我也无法重现您的问题。我在 IST 时区。两种方法都为我返回相同的结果。我使用 Java 7_67 .. 我也尝试使用 Java 8,但结果是一样的。此外,将小时、分钟、秒和毫秒重置为 0 是获取午夜时间的正确方法。您是否错过了在此处粘贴代码的任何步骤!
  • 使用-Duser.timezone=America/Los_Angeles 我能够重现您在上面显示的问题。

标签: java date calendar


【解决方案1】:

编辑:更好的解释

当我们调用 setTimeInMillis() 时,传入的 long 值假定为 UTC。它使用用户的时区或日历对象已经可用的任何时区来计算日历对象时间中的时间。

如果没有传递时区,则在日历对象上设置用户的时区。因此,被认为处于 PST 时区的值“1437004800000L”和时间是使用 PST 时区计算的。 在以毫秒为单位设置时间后调用 setTimeZone() 正在更改 Calendar 对象中的时区。所以日历对象时间被移动到 UTC。这是 runTestMidnight1() 的情况。这就是我们看到日期发生变化的原因,因为原始时间是太平洋标准时间。

在第二种方法中,时区首先由 setTimeZone() 设置为 UTC。所以时间值是'1437004800000L'并且考虑的时区是UTC并且计算的时间将是UTC。而且您只获得 UTC 时间。

只有当用户处于 UTC 时区之后的时区时,才会重现此问题。

希望它澄清。

【讨论】:

    最近更新 更多