Java 8 LocalDate: определить, какой понедельник месяца (1-й, 2-й, 3-й и т. д.)

Я пытаюсь выяснить, как определить, является ли конкретный понедельник первым, вторым, третьим или четвертым понедельником данного месяца. Я понял, как получить следующий понедельник и неделю месяца, которые находятся в классе LocalDate.

LocalDate now = LocalDate.of(2018, 2, 1);
LocalDate nextMonday = now.with(next(DayOfWeek.MONDAY));
WeekFields weekFields = WeekFields.of(Locale.getDefault());
int week = nextMonday.get(weekFields.weekOfMonth());

В приведенном выше примере код получает следующий понедельник и неделю, в которой он находится. Неделя — это вторая неделя февраля, но этот понедельник — не второй понедельник, а первый. Любая помощь в этом будет принята с благодарностью.


person Joseph Freeman    schedule 08.01.2018    source источник
comment
java2s.com/Tutorials/Java/java.time/LocalDate/ index.htm   -  person Hackerman    schedule 08.01.2018
comment
Это первый понедельник первых 7 дней, второй понедельник следующих 7 дней и т. д. Итак, вычтите 1 из числа месяца, разделите на 7 и снова прибавьте 1.   -  person Andy Turner    schedule 08.01.2018
comment
@AndyTurner Я не хочу делить данную дату на число, не могли бы вы привести пример, пожалуйста?   -  person Joseph Freeman    schedule 08.01.2018
comment
Интересный вопрос: есть ли у java.time что-то похожее на Calendar.DAY_OF_WEEK_IN_MONTH? Для установки дня недели в месяце, например, для изменения понедельника на 3-й понедельник месяца: TemporalAdjusters.dayOfWeekInMonth. Но для получения?   -  person Ole V.V.    schedule 08.01.2018


Ответы (3)


Изменить: вам следует взять ответ Оле В. В. (ChronoField.ALIGNED_WEEK_OF_MONTH) вместо этого ответа.

Вы можете выяснить, какой сегодня день месяца, разделить на 7 и прибавить 1:

LocalDate nextMonday = now.with(next(DayOfWeek.MONDAY));
int weekNo = ((nextMonday.getDayOfMonth()-1) / 7) +1;

Например, этот код:

LocalDate now = LocalDate.of(2018, 2, 1);
for(int i = 0; i < 10; i++) {
    now = now.with(next(DayOfWeek.MONDAY));
    int weekNo = ((now.getDayOfMonth()-1) / 7) +1;
    System.out.println("Date : " + now + " == " + weekNo);
}

Распечатывает следующее, что, я думаю, вы хотите...

Date : 2018-02-05 == 1
Date : 2018-02-12 == 2
Date : 2018-02-19 == 3
Date : 2018-02-26 == 4
Date : 2018-03-05 == 1
Date : 2018-03-12 == 2
Date : 2018-03-19 == 3
Date : 2018-03-26 == 4
Date : 2018-04-02 == 1
Date : 2018-04-09 == 2
person Todd    schedule 08.01.2018
comment
Вы должны вычесть 1 перед делением на 7, иначе вы скажете, что 7-е число месяца не находится на той же неделе, что и 1-е число месяца. - person Andy Turner; 08.01.2018
comment
@AndyTurner, отличный улов, исправлено. - person Todd; 08.01.2018
comment
Я считаю, что уловка @AndyTurner очень хорошо иллюстрирует опасность использования собственной арифметики для таких задач, как эта. Вместо этого я рекомендую вам использовать встроенный функционал. - person Ole V.V.; 08.01.2018

Вы не должны использовать для этого собственную арифметику. Используйте встроенный функционал:

    int week = nextMonday.get(ChronoField.ALIGNED_WEEK_OF_MONTH);

С датой из вашего кода это дает:

1

Это означает, что понедельник, который вы получили (5 февраля), является 1-м понедельником месяца. Также вам не нужны никакие WeekFields, потому что первый понедельник месяца является первым понедельником месяца, независимо от того, какой день является первым днем ​​недели или как нумеруются недели в году.

Арифметика дат часто сложна, и в ней легко ошибиться. Это также означает, что даже если вы, наконец, сделаете правильный расчет, читателю будет трудно убедить себя в правильности вашего кода. Поэтому всегда лучше использовать встроенную функциональность, которой мы доверяем тестирование и которая использует поясняющие имена, а не числа, такие как 1 и 7.

Вы можете быть удивлены тем, что используемое поле называется ALIGNED_WEEK_OF_MONTH. Думайте о выровненных неделях как о неделе, которая начинается 1-го числа месяца, независимо от дня недели, и следующие недели сразу друг за другом. В феврале 2018 года первая выровненная неделя проходит с четверга, 1 февраля, по среду, 7 февраля. Следующие выровненные недели начинаются с четвергов, 8, 15 и 22 февраля. Очевидно, что первый понедельник месяца приходится на выровненную неделю 1, 2-й понедельник на выровненной неделе 2 и т. д. Поэтому можно использовать ALIGNED_WEEK_OF_MONTH.

person Ole V.V.    schedule 08.01.2018

Вы можете попробовать несколько раз вычитать неделю из даты, пока месяц не изменится. Тогда количество раз, которое вы это сделали, будет исходной датой «X-й день недели в месяце».

РЕДАКТИРОВАТЬ: Некоторый код.

LocalDate now = LocalDate.of(2018, 2, 1);

int i = 0;
Month originalMonth = now.getMonth();
while (now.getMonth() == originalMonth) { // its an enum, you can do this
    now = now.plusWeeks(-1);
    i++;
}

System.out.println(i); // prints 1
person ifly6    schedule 08.01.2018