javacalendar

Why is Java Calendar saying the first Thursday of the month is Week 5?


I am trying to figure out how to make an alarm on a specific day of the week. Here is sample code that prints what I expect:

  Calendar calendar = Calendar.getInstance();
  calendar.set(Calendar.WEEK_OF_MONTH, 1);
  System.out.println("Week of month: " + calendar.get(Calendar.WEEK_OF_MONTH)); // Week of month: 1

But when I add this to the code I get a result I dont understand:

  Calendar calendar = Calendar.getInstance();
  calendar.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
  calendar.set(Calendar.WEEK_OF_MONTH, 1);
  System.out.println("Week of month: " + calendar.get(Calendar.WEEK_OF_MONTH)); // Week of month: 5

I am trying to set an alarm for a specific day in a specific week. The date today is June 15th, 2013. Could someone please explain this to me. Joda time is not an option with what I am doing, so I need to make this work with the regular Java libs. Thanks for all the help.

Editor's note

The above code will return different results based on the date it is run, because Calendar.getInstance() returns a Calendar set to the current time. For an illustration of the problem that is independent of the current time, use this:

DateFormat dateFormat = DateFormat.getDateInstance();
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(1371294000000L);
System.out.println(dateFormat.format(calendar.getTime()));
calendar.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
System.out.println(dateFormat.format(calendar.getTime()));
calendar.set(Calendar.WEEK_OF_MONTH, 1);
System.out.println("Week of month: " + calendar.get(Calendar.WEEK_OF_MONTH)); 
System.out.println(dateFormat.format(calendar.getTime()));

Which outputs something like this (at least if your default locale is using the Gregorian Calendar):

Jun 15, 2013
Jun 13, 2013
Week of month: 5
May 30, 2013

Solution

  • Java Calendar is lenient by default. If you set it to January 32, it will translate that to Feb 1. Furthermore, the definition of "week" is Locale-specific and a bit confusing. This is explained in great detail in the JavaDocs linked to above.

    In your particular case (or at least in my Locale), a week is defined as starting on Sunday and the "first week of June" is defined as the Sunday through Saturday period that includes June 1. June 1, 2013, is a Saturday, the last day in week 22 of the year 2013. Sunday, June 2, 2013, is the first day of week 2 of June, not the second day of week 1.

    Since there is no Thursday in week 1 of June, the lenient calendar interprets DAY_OF_WEEK, THURSDAY to be the Thursday of week 22 of year 2013, which is May 30, 2013, which is week 5 of May, so you get 5 and not 1 in your output.

    To set the first Thursday in June, you want:

     Calendar calendar = Calendar.getInstance();
     calendar.set(Calendar.MONTH, Calendar.JUNE);
     calendar.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
     calendar.set(Calendar.DAY_OF_WEEK_IN_MONTH, 1);
    

    DAY_OF_WEEK_IN_MONTH can be a bit confusing. You use it with DAY_OF_WEEK to specify which occurrence of that day in that month you want. So when DAY_OF_WEEK is set to Thursday, DAY_OF_WEEK_IN_MONTH, 1 is the first Thursday of the month, DAY_OF_WEEK_IN_MONTH, 2 is the second Thursday of the month, etc. This is much less Locale-specific or open to (mistaken) interpretation then what days constitute the first week of the month.