Why does GregorianCalendar.getTimeInMillis() return wrong value?

I want to calculate time gap in form of days between two dates. So I wrote following code:

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;
}

Here are outputs:

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

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

The parameters of this method, start and end, come from following code:

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

So the behavior is very strange. I thought that gregorianCalendar.getTimeInMillis() will always return correct and timezone-independent value so that it can be used as a stable and trustworthy standard to compute and compare time. However, as the code and output shown, it is wrong. And if I considered my timezone(Beijing GMT+8:00), add an offset by Timezone.getDefault().getRawOffset() to parameter start or minus it to end, the result will be correct. But this is actually strange.

What made me get incorrect result?

UPDATE:

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(" ");
}
Jon Skeet
people
quotationmark

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

Yes, and that's entirely reasonable. You're comparing March 2nd, 15:28:08 with March 3rd, 01:00:00. They're on different dates, but are less than a day apart. There's no problem here.

Heck, you could see the same with two values which are just one millisecond apart - they can still be on different dates.

Note that in terms of time zones, by default GregorianCalendar will use your system default time zone. The value returned by getTimeInMillis will always return the number of milliseconds since the Unix epoch, so that's time zone neutral - but the constructor you're calling here:

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

... is saying "I want the value representing March 3rd, 1am local time" where local time means "in your default time zone".

Computing the difference between dates is difficult - I would very strongly recommend that you ditch Calendar and Date entirely, instead using java.time if you're using Java 8, or Joda Time otherwise. Then you can use LocalDate to represent a date without a time, and easily find the difference between two dates.

people

See more on this question at Stackoverflow