简体   繁体   中英

Get weeks between 2 dates with weeks crossing 2 months

I would like to get all the weeks between 2 dates with weeks that cross 2 months counted twice for each month. For example, in 2021 week 14 of the year hosted both March and April so in this case, I would like that week counted twice (once for March and once for April). I've looked and found just libraries that count the number of weeks between 2 dates. I think I could get week numbers and month numbers and form a unique array but this seems a bit over the top. Has anyone got any suggestions?

weeks that cross 2 months counted twice

The code below allows to do that by utilizing only the standard LocalDate class and it's methods isBefore() , plusWeeks() , plusDays() .

Keep in mind the days of the week and months are represented by enums from the java.time package.

I've made a couple of assumptions :

  • week starts with Sunday ;
  • chunks of the week at the start and at the end of the given period have to be taken into account as well as full-length weeks.
    public static void main(String[] args) {
        System.out.println(getWeekCount(LocalDate.of(2022, 1, 1),
                                        LocalDate.of(2022, 2, 1)));

        System.out.println(getWeekCount(LocalDate.of(2022, 1, 1),
                                        LocalDate.of(2022, 3, 1)));
    }
    public static int getWeekCount(LocalDate date1, LocalDate date2) {
        int weekCount = 0;
        LocalDate cur = date1;
        LocalDate finish = date2;
        // assumption: week starts with sunday
        // assumption: chunk of week at the start and at the end have to be taken into account as well as full weeks
        if (cur.getDayOfWeek() != DayOfWeek.SUNDAY) { // adjusting current date
            LocalDate next = cur.plusDays(DayOfWeek.SUNDAY.ordinal() - cur.getDayOfWeek().ordinal() + 1);
            weekCount += getWeeksIncrement(cur, next);
            cur = next;
        }
        if (finish.getDayOfWeek() != DayOfWeek.SUNDAY) { // adjusting finish date
            LocalDate previous = finish.minusDays(finish.getDayOfWeek().ordinal() + 1);
            weekCount += getWeeksIncrement(previous, finish);
            finish = previous;
        }
        while (cur.isBefore(finish) || cur.equals(finish)) {
            LocalDate next = cur.plusWeeks(1);
            weekCount += getWeeksIncrement(cur, next);
            cur = next;
        }
        return weekCount;
    }
    public static int getWeeksIncrement(LocalDate cur, LocalDate next) {
        return weekIsSharedBetweenTwoMonth(cur, next) ? 2 : 1;
    }
    public static boolean weekIsSharedBetweenTwoMonth(LocalDate cur, LocalDate next) {
        return next.getMonth() != cur.getMonth() &&
                next.withDayOfMonth(1).isAfter(cur);
    }

Output

7   -  weeks between: 2022-01-01 and 2022-02-01
12  -  weeks between: 2022-01-01 and 2022-03-01

Well, this is achievable with a combination of the Java Date and Time API ( java.time ) and the Java Streams API ( java.util.stream ):

long weeksBetween(LocalDate start, LocalDate endInclusive) {
    LocalDate normalizedStart = start.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
    LocalDate normalizedEndExclusive = endInclusive.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
    return Stream.iterate(normalizedStart, d -> d.plusWeeks(1))
        .takeWhile(d -> d.isBefore(normalizedEndExclusive))
        .mapToInt(d -> d.getMonthValue() == d.plusDays(6).getMonthValue() ? 1 : 2)
        .sum();
}

What happens here, is as follows.

First, the dates are normalized , that is, they are set to the start of the week (Monday according to ISO standards).

Then we walk over the Monday of each week, and check if its last day of the week (Sunday) lies within the same month. If it is, then it yields 1 , otherwise it yields 2 .

At last, we sum all yielded values.


Note that I assumed that a week starts on Monday (ISO). The code also considers the week of both the start date as the end date as full ones.

You can get the weeknumber like this using java.time:

LocalDate date = LocalDate.of(year, month, day);
int weekOfYear = date.get(ChronoField.ALIGNED_WEEK_OF_YEAR);

You did not mention which java version you are using. java.time was introduced in java 8. There are other solutions available for pre-java 8.

Based on the above, you should be able to solve your problem.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM