Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird behavior of Calendar

I am facing a weird behavior of java.util.Calendar.

The problem is when I add a method call Calendar#getTime() in between only than I get correct result but when I directly get the Dates of the week without calling Calendar#getTime() it refers to the next week instead of current week.

Please consider following code snippet :

public class GetDatesOfWeek {

    public static void main(String[] args) {

        SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");

        Calendar cal = Calendar.getInstance();
        cal.set(1991, Calendar.DECEMBER, 11);

        //System.out.println(cal.getTime());//LINE NO : 14

        for(int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) {
            cal.set(Calendar.DAY_OF_WEEK, i);
            Date date = cal.getTime();
            System.out.println(sdf.format(date));
        }
    }

}

When I uncomment the line no 14 I get following output :

Wed Dec 11 07:38:06 IST 1991
08-12-1991
09-12-1991
10-12-1991
11-12-1991
12-12-1991
13-12-1991
14-12-1991

But when I comment that line and execute the code I get following output.

15-12-1991
16-12-1991
17-12-1991
18-12-1991
19-12-1991
20-12-1991
21-12-1991

Note that in both the cases month and year fields are proper but the start date changed from 08-12-1991 to 15-12-1991 for me 08-12-1991 is correct.

My Question :

  • Why I am getting different dates of the week in the above two cases ?
  • Why this kind of functionality is provided in Java ?
like image 334
akash Avatar asked Aug 12 '15 02:08

akash


1 Answers

If you step through your code in a debugger, you will see interesting things happen to the internal variables of the cal object.

The calendar object stores both the time it represents and adjustment fields separately. After executing line 12, the cal object is constructed with the current time and no adjustment fields, and its internal variable isTimeSet is set to true. This means that the internal time stored is correct.

Executing line 13 clears isTimeSet to false because now the internal time needs to be adjusted by the adjustment fields before it is accurate again. This doesn't happen immediately, rather it waits for a call to getTime() or get(...), getTimeInMillis(), add(...) or roll(...).

Line 14 (if uncommented) forces the internal time to be recalculated using the adjustment fields, and sets the isTimeSet variable to true again.

Line 17 then sets another adjustment field, and unsets the isTimeSet variable again.

Line 18 recalculates the correct time again, based on the adjustment field set in line 17.

The solution:

The problem occurs when you combine setting the day-of-month with the day-of-week. When the day-of-week is set, the day-of-month setting is ignored, and the current day-of-month in the cal object is used as a starting point. You happen to be getting a week starting on the 15th because today's date is the 12th and 15th Dec 1991 is the nearest Sunday to 12th Dec 1991.

Note: If you run your test again in a week's time, you will get a different result.

The solution is to call getTimeInMillis() or getTime() to force recalculation of the time before setting day-of-week adjustments.

Testing:

If you don't want to wait a week to test again, try the following:

public class GetDatesOfWeek {

    public static void main(String[] args) {

        SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy");

        Calendar cal = Calendar.getInstance();
        cal.set(2015, Calendar.AUGUST, 1); // adjust this date and see what happens
        cal.getTime();

        cal.set(1991, Calendar.DECEMBER, 11);
        //System.out.println(cal.getTime());//LINE NO : 14

        for(int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) {
            cal.set(Calendar.DAY_OF_WEEK, i);
            Date date = cal.getTime();
            System.out.println(sdf.format(date));
        }
    }

}
like image 111
Jason Avatar answered Nov 03 '22 00:11

Jason