javadatetimedayofweekjava-time

How can I find all week numbers of month that are the # week of month in a year with Java?


I am looking to build up a function that return an array with all week numbers of the previous months in a year that are the same week number of one particular month.

I am using as first day of week Monday and I am taking as first week of month week with the first Monday of current month.

Input: week of year and year. For example, 27 and 2019. The first week of July (7).

Output: array of week of months. For example, [2, 6, 10, 14, 19, 23, 27].

What I try:

private void getResult(int weekYear)
{
    LocalDate date = LocalDate.now();
    final int weekNumber = 27;
    LocalDate newDate = date.with(IsoFields.WEEK_OF_WEEK_BASED_YEAR, weekNumber);
    int month = newDate.getMonthValue();;
        
    int weekMonth = LocalDate.from(newDate).get(WeekFields.ISO.weekOfMonth());
        
    System.out.println(newDate);
    System.out.println(month);
    System.out.println(weekMonth);
        
    ArrayList<Integer> list = new ArrayList<Integer>();
        
    for (int i = 1; i <= month; i++)
    {
        LocalDate tempDate = date.withYear(2019).withMonth(i).with(WeekFields.ISO.weekOfMonth(), weekMonth);
        int tempYear = LocalDate.from(tempDate).get(WeekFields.ISO.weekOfWeekBasedYear());
        list.add(tempYear);
    }
        
    list.forEach((e) -> System.out.print(e + " "));
}
int weekYear = 27;
getResult(weekYear);

What I get: [1 6 10 14 18 23 27].

What I am looking for: I have two question:

  1. First one: the results obtained are different from those expected. I think the problem is due to the fact that I didn't specify how to calculate the first week of the month (first Monday of the month). Is it right? How can I solve that?
  2. Second one: What is a better solution?

Solution

  • I think the key here is understanding a few points (hope that I have understood them correctly from your question).

    1. You are numbering weeks in two different ways. For the week of year you are using ISO numbering: the first week of the year is the one that includes at least 4 days of the new year. For week of month you are counting the Mondays (you may say that the first week of the month is the one that includes seven days of the month, not four).
    2. The week number may not always exist. If your starting point is in 0th or the 5th week of the month, a preceding month may not have that week in it.

    the results obtained are different from those expected. I think the problem is due to the fact that I didn't specify how to calculate the first week of the month (first Monday of the month). Is it right? How can I solve that?

    I believe you are correct. To count the Mondays of the month you may use:

            LocalDate tempDate = date.withYear(2019)
                    .withMonth(i)
                    .with(ChronoField.DAY_OF_WEEK, DayOfWeek.MONDAY.getValue())
                    .with(ChronoField.ALIGNED_WEEK_OF_MONTH, weekMonth);
    

    (DayOfWeek.MONDAY.getValue() is just of wordy way of saying 1, of course, but conveys the intention better, so I prefer it.)

    With this change to your code the output is the expected:

    2 6 10 14 19 23 27

    The key is ChronoField.ALIGNED_WEEK_OF_MONTH. The aligned weeks of a month start from the 1st of the month and are always 7 days regardless of the days of the week. The first aligned week is from the 1st through the 7th of the month, the 2nd aligned week if from 8th through 14th, etc. Since we have set the day of week to Monday, setting the aligned week to 1 gives us the 1st Monday of the month, etc.

    We’re done yet, though. If I set weekNumber to 40, I get

    2 6 10 14 14 23 27 27 36 41

    I had expected 40 to be the last number in the list, but it is not there. Week 40 of 2019 is from Monday September 30 through October 6, so if I understand correctly you want the 5th week of those months that have a 5th week. This brings us back to the issue of not all month having a week 5 (because they don’t have 5 Mondays). What happened was that since I ran your code on a Tuesday, it took Tuesday in week 40, which is October 1, as a starting point, and therefore gave me the 1st rather than the 5th week of every month.

    are there better solutions? Can you suggest one?

    I can’t really. I think that what you’ve got is fine.

    Only you’re not using the int weekYear parameter. You may want to use it in place of your weekNumber local variable. In any case you should delete one of them and use the other.

    And this unrelated tip: Your use of LocalDate.from(someLocalDate) is redundant since it just gives you the same LocalDate again (either the same object or an equal one, I don’t know or care). Just use someLocalDate in those situations.