Search code examples
javacalendargregorian-calendar

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


Solution

  • 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