Как узнать количество будних дней в месяце с помощью LocalDate - Java - PullRequest
0 голосов
/ 29 апреля 2018

Как мне узнать количество будних дней (с понедельника по пятницу) в месяце с LocalDate? Я никогда не использовал java.time раньше, поэтому я не знаю всех его функций. Я искал на этом сайте безрезультатно вместе с поиском ответа. Я также не хочу использовать какие-либо внешние библиотеки.

Пример: по состоянию на этот месяц, апрель 2018 года, 21 рабочий день. А в следующем месяце 23 рабочих дня.

Ответы [ 2 ]

0 голосов
/ 29 апреля 2018

При желании, см. Оптимизированное решение о грубой силе в конце

Вот реализация без перебора для вычисления дней недели (пн-пт) в месяце.

Используется YearMonth вместо LocalDate, поскольку значение дня месяца не имеет смысла для расчета.

public static int weekDaysInMonth(YearMonth yearMonth) {
    int len = yearMonth.lengthOfMonth(); // 28-31, supporting leap year
    int dow = yearMonth.atDay(1).getDayOfWeek().getValue(); // 1=Mon, 7=Sun
    return (dow <= 5 ? Math.min(len - 8, 26 - dow) : Math.max(len + dow - 16, 20));
}

Вот перегрузка, принимающая LocalDate, поэтому легко позвонить, если это то, что у вас есть.

public static int weekDaysInMonth(LocalDate date) {
    return weekDaysInMonth(YearMonth.from(date));
}

Test

System.out.println(weekDaysInMonth(LocalDate.parse("2018-04-15"))); // April 15, 2018
System.out.println(weekDaysInMonth(YearMonth.of(2018, 5)));         // May 2018

выход

21
23

Объяснение формулы

Формула в выражении return была создана путем проверки ожидаемого возвращаемого значения для каждой комбинации len (количество дней в месяце, 28 - 31) и dow (день недели первого дня). месяца, 1 = понедельник - 7 = солнце):

   |  1   2   3   4   5    6   7
   | Mo  Tu  We  Th  Fr   Sa  Su
---+----------------------------
28 | 20  20  20  20  20   20  20
29 | 21  21  21  21  21   20  20
30 | 22  22  22  22  21   20  21
31 | 23  23  23  22  21   21  22

Пояснение к dow <= 5 (пн-пт)

Первоначально есть len - 8 будних дней, то есть мы вычитаем 4 выходных, которые всегда существуют в месяце.

Когда мы доберемся до четверга и пятницы, нам нужно ограничить это на 1 или 2 выходных дня, которые мы теряем. Если вы посмотрите на 31-дневную строку, мы ограничим ее 26 - dow, то есть для пятницы (dow=5) мы ограничим 21, а для четверга (dow=4) - 22. Для понедельника-среды мы также ограничиваем, но верхний предел равен или превышает первоначальный расчет, поэтому это не имеет значения.

Покрытие выполняется методом min(xxx, cap), поэтому мы получаем:

min(len - 8, 26 - dow)

Объяснение для dow >= 6 (Сб-Вс)

Вы видите маленький треугольник в правом нижнем углу. Если мы расширим этот шаблон, мы получим:

   |  4   5   6   7
---+---------------
28 | 16  17  18  19
29 | 17  18  19  20
30 | 18  19  20  21
31 | 19  20  21  22

Как формула, это len + dow - 16.

Сравнивая это с исходной сеткой, числа опускаются до 20, что делается методом max(xxx, bottom), поэтому мы получаем:

max(len + dow - 16, 20)

Заключение

Наконец, мы объединяем два, используя троичный условный оператор:

dow <= 5  ?  min(len - 8, 26 - dow)  :  max(len + dow - 16, 20)

Тогда полный оператор Java будет:

return (dow <= 5 ? Math.min(len - 8, 26 - dow) : Math.max(len + dow - 16, 20));

Решение по грубой силе

Если вы предпочитаете решение методом грубой силы, вы можете облегчить его, пропустив первые 4 недели, которые всегда существуют в месяце:

public static int weekDaysInMonth(LocalDate refDate) {
    LocalDate firstOfMonth = refDate.withDayOfMonth(1);
    LocalDate nextMonth = firstOfMonth.plusMonths(1);
    int days = 20;
    for (LocalDate date = firstOfMonth.plusDays(28); date.isBefore(nextMonth); date = date.plusDays(1))
        if (date.getDayOfWeek().getValue() <= 5) // 1=Mon - 5=Fri, i.e. not 6=Sat and 7=Sun
            days++;
    return days;
}
0 голосов
/ 29 апреля 2018

Итак, спасибо Президенту Dreamspace за помощь в поиске решения (даже если это грубая сила), когда он сказал следующее:

Создать LocalDate для первого дня соответствующего месяца. Выполните цикл до конца месяца, используя ld.plusDays (1), и используйте ld.getDayOfWeek (), чтобы увидеть, с каким DayOfWeek вы имеете дело.

Вот что я нашел для работы:

public static int businessDaysInMonth(final LocalDate ld) {

    int weekDays = 0;
    LocalDate date = ld.withDayOfMonth(1);
    final int intendedMonthValue = ld.getMonthValue();
    do {
        final DayOfWeek dayOfWeek = date.getDayOfWeek();

        if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) {
            weekDays++;
        }

        date = date.plusDays(1);
    } while (date.getMonthValue() == intendedMonthValue);

    return weekDays;
}
...