Java Calendar shows wrong amount of weeks when first week of month is defined as week in which 1 occurs

I've made a function that should get the number of weeks for a given month. For January, May, July and October, it should return 5 weeks. However, it returns 5 for March, June, September. and November. Surprisingly, the total amount of weeks are correct (52).

public static int getNofWeeksWithFirstWeekStartingWhenFirstDayOfMonthOccurs(Calendar calendar) {

    while (calendar.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
        calendar.roll(Calendar.DATE, true);
    }
    int currentMonth = calendar.get(Calendar.MONTH);
    int nextMonth = (currentMonth + 1) % 12;
    int prePreviousMonth = (currentMonth + 12 - 2) % 12;
    int nofWeeks = 0;
    do {
        int month = calendar.get(Calendar.MONTH);
        if (month == nextMonth) {
            nofWeeks++;
        }
        if (month == prePreviousMonth) {
            break;
        }
        calendar.roll(Calendar.WEEK_OF_YEAR, true);
    } while (true);
    return nofWeeks;
}

public static void main(String[] args) {

    int numWeeks;
    int totalWeeks=0;
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.YEAR, 2016);
    calendar.set(Calendar.DAY_OF_MONTH, 1);

    calendar.set(Calendar.MONTH, Calendar.MAY);
    numWeeks=getNofWeeksWithFirstWeekStartingWhenFirstDayOfMonthOccurs(calendar);
    System.out.println("no of weeks " + numWeeks);
}

Output: no of weeks 4

Month start is in Week that has first day. e.g.:

25 - 1 May
2 - 8 May
9 - 15 May
16 - 22 May
23 - 29 May

5 Weeks in May:

enter image description here

Jon Skeet
people
quotationmark

It sounds to me like you should:

  • Work out the first day of the month
  • Determine from that how many "extra" days are "borrowed" from the previous month (e.g. 0 if day 1 is a Monday; 1 if day 1 is a Tuesday etc)
  • Add that to the number of days in the regular month
  • Divide by 7 (with implicit truncation towards 0)

There's no need to do half the work you're currently doing.

Using java.util.Calendar, it would be something like:

// Note: day-of-week runs from Sunday (1) to Saturday (7).
// Entry 0 here is not used. We could do this without an array lookup
// if desired, but it's whatever code you think is clearest.
private static final int[] EXTRA_DAYS = { 0, 6, 0, 1, 2, 3, 4, 5 };

// Note: 0-based month as per the rest of java.util.Calendar
public static int getWeekCount(int year, int month) {
    Calendar calendar = new GregorianCalendar(year, month, 1);
    int dayOfWeekOfStartOfMonth = calendar.get(Calendar.DAY_OF_WEEK);
    int extraDays = EXTRA_DAYS[dayOfWeekOfStartOfMonth];
    int regularDaysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    int effectiveDaysInMonth = regularDaysInMonth + extraDays;
    return effectiveDaysInMonth / 7;
}

If at all possible, I'd recommend using Joda Time or java.time instead, however.

With that code, the results for 2016 are:

  • January: 5
  • February: 4
  • March: 4
  • April: 4
  • May: 5
  • June: 4
  • July: 5
  • August: 4
  • September: 4
  • October: 5
  • November: 4
  • December: 4

people

See more on this question at Stackoverflow