Например:
5 February 2016
- первая неделя,12 February 2016
- вторая неделя,28 February 2016
- на прошлой неделе
Например:
5 February 2016
- первая неделя,12 February 2016
- вторая неделя,28 February 2016
- на прошлой неделеНу, не совсем понятно, как вы определяете неделю месяца. Для дальнейшего обсуждения я предполагаю, что вы говорите о неделях, начинающихся в понедельник (как в стандарте ISO-8601 и в большинстве европейских стран).
Есть два возможных способа определения того, как обрабатывать начало и конец месяца при подсчете недель.
Поскольку начало недели в понедельник не обязательно совпадает с первым днем месяца, неделя может начинаться в предыдущем месяце или относиться к следующему месяцу.
Классы JDK SimpleDateFormat
с символом шаблона поля w (а также новое поле JSR-310 WeekFields.weekOfMonth()) используют следующую стратегию:
Если первый день месяца приходится на период с понедельника по четверг, то соответствующая неделя имеет не менее 4 дней в текущем месяце и будет считаться неделей 1, в противном случае - неделей 0 (нулем). Последовательно последний день месяца всегда будет использовать возрастающее число, даже если он относится к первой неделе следующего месяца.
В отличие от этого определения, спецификация шаблона даты и времени CLDR и ISO-8601 почти ничего не говорят о деталях в контексте недели месяца. Однако эти стандарты не умалчивают о неделе года, когда они описывают другую стратегию. И CLDR явно говорит о неделе месяца (раздел 8.4 ):
8.4 Неделя года
Значения, рассчитанные для поля «Неделя года», находятся в диапазоне от 1 до 53 для григорианского календаря (они могут иметь другие диапазоны для других календарей). Неделя 1 в году – это первая неделя, содержащая как минимум указанное минимальное количество дней в этом году. Недели между 1-й неделей одного года и 1-й неделей следующего года нумеруются последовательно от 2 до 52 или 53 (при необходимости). Например, 1 января 1998 года был четверг. Если первый день недели — ПОНЕДЕЛЬНИК, а минимальное количество дней в неделе — 4 (это значения, отражающие ISO 8601 и многие национальные стандарты), то первая неделя 1998 года начинается 29 декабря 1997 года и заканчивается 4 января. 1998 г. Однако, если первый день недели - ВОСКРЕСЕНЬЕ, то первая неделя 1998 г. начинается 4 января 1998 г. и заканчивается 10 января 1998 г. Тогда первые три дня 1998 г. являются частью 53-й недели 1997 г.
Значения рассчитываются аналогичным образом для недели месяца.
Разница между обеими стратегиями, примененными на дату 2016-02-29, составит:
Теперь я представляю решение для Joda-Time.
public static void main(String[] args) throws Throwable {
System.out.println(getWeekOfMonth(false)); // CLDR/ISO-spec
// 1 for today=2016-02-05
// 2 for today=2016-02-12
// 4 for today=2016-02-28
// 1 for today=2016-02-29
System.out.println(getWeekOfMonth(true)); // JDK-behaviour
// 1 for today=2016-02-05
// 2 for today=2016-02-12
// 4 for today=2016-02-28
// 5 for today=2016-02-29
}
private static int getWeekOfMonth(boolean bounded) {
int weekOfMonth;
LocalDate today = LocalDate.now();
LocalDate first = today.dayOfMonth().withMinimumValue();
int dowFirst = first.getDayOfWeek();
if (dowFirst <= DateTimeConstants.THURSDAY) {
// we are in week 1 and go to Monday as start of week
first = first.minusDays(dowFirst - DateTimeConstants.MONDAY);
// first try: we determine the week of current month
weekOfMonth = Days.daysBetween(first, today).getDays() / 7 + 1;
if (!bounded) {
// edge case: are we in first week of next month?
LocalDate next = first.plusMonths(1);
int dowNext = next.getDayOfWeek();
if (dowNext <= DateTimeConstants.THURSDAY) {
next = next.minusDays(dowNext - DateTimeConstants.MONDAY);
if (!next.isAfter(today)) {
weekOfMonth = 1;
}
}
}
} else if (bounded) {
weekOfMonth = 0;
} else {
// we are in last week of previous month so let's check the start of previous month
LocalDate previous = first.minusMonths(1);
int dowPrevious = previous.getDayOfWeek();
if (dowPrevious <= DateTimeConstants.THURSDAY) {
previous = previous.minusDays(dowPrevious - DateTimeConstants.MONDAY);
} else {
previous = previous.plusDays(DateTimeConstants.MONDAY - dowPrevious + 7);
}
weekOfMonth = Days.daysBetween(previous, today).getDays() / 7 + 1;
}
return weekOfMonth;
}
Надеюсь, это не слишком сложно для вас.
Кстати, если вам интересно, как выглядят простые альтернативы, применимые на платформах старше Java-8:
Time4J (моя библиотека)
private static int time4j(boolean bounded) { // supports both definitions
PlainDate today = SystemClock.inLocalView().today(); // using system timezone
return today.get(
(bounded ? Weekmodel.ISO.boundedWeekOfMonth() : Weekmodel.ISO.weekOfMonth()));
}
private static int threeten() { // only JDK-definition (code similar to Java-8)
org.threeten.bp.LocalDate today = org.threeten.bp.LocalDate.now();
return today.get(WeekFields.ISO.weekOfMonth());
}
private static int oldJDK() { // only JDK-definition
GregorianCalendar gcal = new GregorianCalendar();
gcal.setMinimalDaysInFirstWeek(4);
gcal.setFirstDayOfWeek(Calendar.MONDAY);
return gcal.get(Calendar.WEEK_OF_MONTH);
}
Как видите, с этими альтернативами очень легко изменить базовые недельные модели на случаи, отличные от ISO (например, американские недели). Если вы хотите, чтобы это было в Joda-Time, то я оставляю вам задачу переписать представленное Joda-решение.
Обновление в связи с комментарием ниже по теме:
Итак, все дело в дне недели в месяце. Joda-Time также не поддерживает этот элемент/поле из коробки. Извини. Но вы можете изучить необходимый алгоритм для такого поля используется в других библиотеках.
Демонстрационный пример в Time4J для моделирования правила rfc2445, упомянутого в комментарии:
PlainDate dtStart = PlainDate.of(2016, Month.FEBRUARY, 4);
int count = 5;
Weekday byday = Weekday.FRIDAY; // first
int interval = 1;
CalendarUnit frequency = CalendarUnit.MONTHS;
List<PlainDate> sequence = new ArrayList<>(count);
PlainDate wim = dtStart;
for (int i = 0; i < count; i++) {
wim = wim.with(PlainDate.WEEKDAY_IN_MONTH.setToFirst(byday));
sequence.add(wim);
wim = wim.with(PlainDate.DAY_OF_MONTH, 1).plus(interval, frequency);
}
if (!sequence.isEmpty() && !sequence.get(0).equals(dtStart)) {
sequence.remove(0); // Not quite sure - do you need another condition?
}
System.out.println(sequence); // [2016-03-04, 2016-04-01, 2016-05-06, 2016-06-03]
В Java-8 также есть поддержка через специализированный корректор, чтобы вы могли легко перенести данный демонстрационный пример на Java-8 с помощью java.time.LocalDate
.
1st monday
, 2nd monday
, last monday
на 1MO,2MO,-1MO
. Чтобы узнать, есть ли DTSTART среди этих понедельников, и оставить его, а если нет, то удалить его, я должен знать, какой день и на какой неделе DTSTART.
- person Anatoly; 05.02.2016
SimpleDateFormat
-символ F шаблона или специальный JSR-310-adjuster или в Time4J этот интерфейс.. Для Joda-Time такого эквивалента тоже нет, поэтому необходим другой обходной путь.
- person Meno Hochschild; 06.02.2016
FREQ=MONTHLY;COUNT=5;BYDAY=1FR;INTERVAL=1;
Где DTSTART — 04 февраля 2016 г. Т.е. каждый месяц, первую пятницу месяца следует выбирать 5 раз, даты: 5 февраля 2016 г., 4 марта 2016 г., 1 апреля 2016 г., 6 мая 2016 г., 3 июня 2016 г. Но пользователь выбрал начало этой последовательности 4 февраля 2016 года, что является не 1-й пятницей, а 1-м четвергом. Мне нужно проверить, что это и оставить его или удалить. Я реализую ваши примеры завтра, чтобы увидеть, что мне больше подходит, спасибо. Единственная проблема связана с лицензией LPGL вашей библиотеки, не знаю, смогу ли я ее использовать.
- person Anatoly; 06.02.2016
DAY_OF_WEEK_IN_MONTH
, как описано здесь: docs.oracle.com/javase/7/docs/api/java/util/ Мне стыдно, но я неправильно сформулировал свой вопрос. В любом случае, ваш ответ на мой вопрос, как я его задал, был правильным, и, по крайней мере, я надеюсь, что он кому-то поможет.
- person Anatoly; 06.02.2016
В jdk 8 вы можете реализовать свои собственные TemporalAdjusters и использовать с LocalDate.with()
weekOfWeekBasedYear()
, отсутствует вjava.time
. - person Meno Hochschild   schedule 05.02.2016