为什么 GregorianCalendar.getTimeInMillis() return 值错误?

Why does GregorianCalendar.getTimeInMillis() return wrong value?

我想计算两个日期之间的天数形式的时间间隔。所以我写了下面的代码:

public static int calculateTimeGap(long start, long end) {
    GregorianCalendar calendar1 = new GregorianCalendar();
    calendar1.setTimeInMillis(start);
    System.out.println("calendar1:");
    showCalendar(calendar1);

    GregorianCalendar calendar2 = new GregorianCalendar();
    calendar2.setTimeInMillis(end);
    System.out.println("calendar2:");
    showCalendar(calendar2);

    int gap = (int) (end / 86400000 - start / 86400000);
    System.out.println("gap in days:" + gap);
    return gap;
}

输出如下:

03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: calendar1:
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: year: 2016
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: month: 3
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: day: 2
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: week: 3
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: hour: 15
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: minute: 28
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: second: 8
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out:  
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: calendar2:
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: year: 2016
03-02 15:28:08.021 20814-20814/com.ywwynm.everythingdone I/System.out: month: 3
03-02 15:28:08.022 20814-20814/com.ywwynm.everythingdone I/System.out: day: 3
03-02 15:28:08.022 20814-20814/com.ywwynm.everythingdone I/System.out: week: 4
03-02 15:28:08.022 20814-20814/com.ywwynm.everythingdone I/System.out: hour: 1
03-02 15:28:08.022 20814-20814/com.ywwynm.everythingdone I/System.out: minute: 0
03-02 15:28:08.022 20814-20814/com.ywwynm.everythingdone I/System.out: second: 0
03-02 15:28:08.022 20814-20814/com.ywwynm.everythingdone I/System.out:  
03-02 15:28:08.022 20814-20814/com.ywwynm.everythingdone I/System.out: gap in days:0

你可以看到两个 GregorianCalendar 出现了不同的日子,但最后我们得到的差距等于 0。

该方法的参数startend来自以下代码:

GregorianCalendar curCalendar = new GregorianCalendar();
long start = curCalendar.getTimeInMillis(); // parameter start

GregorianCalendar calendar = new GregorianCalendar(2016, Calendar.MARCH, 3, 1, 0);
long end = calendar.getTimeInMillis(); // parameter end

所以这个行为很奇怪。我认为 gregorianCalendar.getTimeInMillis() 将始终 return 正确且与时区无关的值,因此它可以用作计算和比较时间的稳定且值得信赖的标准。但是,如代码和输出所示,这是错误的。如果我考虑我的时区(北京 GMT+8:00),将偏移量添加 Timezone.getDefault().getRawOffset() 到参数 start 或减去它到 end,结果将是正确的。但这其实很奇怪。

是什么让我得到了错误的结果?

更新:

public static void showCalendar(GregorianCalendar calendar) {
    System.out.println("year: "   +  calendar.get(Calendar.YEAR));
    System.out.println("month: "  + (calendar.get(Calendar.MONTH) + 1));
    System.out.println("day: "    +  calendar.get(Calendar.DATE));
    System.out.println("week: "   + (calendar.get(Calendar.DAY_OF_WEEK) - 1));
    System.out.println("hour: "   +  calendar.get(Calendar.HOUR_OF_DAY));
    System.out.println("minute: " +  calendar.get(Calendar.MINUTE));
    System.out.println("second: " +  calendar.get(Calendar.SECOND));
    System.out.println(" ");
}

You can see that two GregorianCalendars present different days, but finally we got a gap equals 0.

是的,这是完全合理的。您将 3 月 2 日 15:28:08 与 3 月 3 日 01:00:00 进行比较。他们在不同的日期,但相隔不到一天。这里没有问题。

哎呀,您可以看到两个值相同,这两个值仅相隔 一毫秒 - 它们仍然可以在不同的日期。

请注意,就时区而言,默认情况下 GregorianCalendar 将使用您的系统默认时区。由 getTimeInMillis 编辑的值 return 将始终 return 自 Unix 纪元以来的毫秒数,因此这是时区中立的 - 但您在这里调用的构造函数:

new GregorianCalendar(2016, Calendar.MARCH, 3, 1, 0)

... 表示 "I want the value representing March 3rd, 1am local time" 其中 当地时间 表示 "in your default time zone".

计算日期之间的差异很困难 - 我 非常强烈 建议您完全放弃 CalendarDate,而是使用 java.time 如果您使用 Java 8,否则 Joda Time。然后你可以用LocalDate表示没有时间的日期,很容易找出两个日期之间的差异。

您要衡量的不是您定义的天数差距。您要测量的是开始和结束之间的完整 24 小时周期数。

在您的示例中,您的结束时间实际上与开始时间相差不到 24 小时。所以你在 24 小时内的距离是 0.

如果您想测量由当前时区午夜定义的天数差异作为天数之间的边界,您应该比较两个 Calendar 对象的 DAY_OF_YEAR and YEAR 值。