Как найти n-е предыдущее воскресенье с помощью API даты и времени Java или Joda-time?
Как рассчитать предыдущее воскресенье или предыдущее предыдущее воскресенье или, в общем, скажем, как узнать воскресенье до n недель? Уловка в том, что если сегодня воскресенье, то он должен вернуться сегодня как воскресенье, а не на прошлой неделе.
Ищете Joda-Time или Java 8 временное решение.
Изменить: я пытался
DateTime sunday = now
.minusWeeks(1)
.withDayOfWeek(DateTimeConstants.SUNDAY)
.withTimeAtStartOfDay();
DateTime previousWeekSunday = now
.minusWeeks(2)
.withDayOfWeek(DateTimeConstants.SATURDAY)
.withTime(23, 59, 59, 999);
но если текущее воскресенье, то эта логика не работает, поскольку она не дает сегодняшнюю дату.
2 ответа
По сути, вам нужно проверить, является ли сегодня воскресенье, если нет, то оглянуться на предыдущую...(или рекурсивно перенести дату назад, если вам нужна предыдущая...)
Используя Java 8 вам понадобится:
LocalDate date = LocalDate.now();
DayOfWeek todayAsDayOfWeek = date.getDayOfWeek();
LocalDate prevSun = todayAsDayOfWeek == DayOfWeek.SUNDAY ? date : date.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
System.out.println(prevSun);
Редактировать: метод previousOrSame пропустит проверку фактического дня недели
LocalDate date = LocalDate.now();
LocalDate prevSun = date.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
prevSun = date.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
System.out.println(prevSun);
На этот вопрос уже был дан подход с использованием Java 8, но для справки, вот решение Joda-Time (а также мои 2 цента для Java 8).
Если я правильно понял, общий алгоритм получения n-го предыдущего воскресенья таков:
- если текущая дата уже воскресенье, вернитесь назад
n-1
недели (так дляn=1
, возвращает ту же дату) - иначе найди n-е предыдущее воскресенье с этой даты
Я создал метод, который получает n
(количество недель) и DateTime
(дата начала с). Код является:
// get the n'th previous Sunday, from the given DateTime
public DateTime nPreviousSunday(int n, DateTime dateTime) {
// avoid zero or negative numbers (optional, see if it fits your use cases)
if (n <= 0) {
return dateTime; // return the same date
}
DateTime d = dateTime;
// get first previous (or same) Sunday
int dow = d.getDayOfWeek();
if (dow != DateTimeConstants.SUNDAY) { // not a Sunday, adjust the day to the previous one
int diff = DateTimeConstants.SUNDAY - dow;
// DateTimeConstants.SUNDAY is 7, so diff is always positive
// d is (7 - diff) days ahead of Sunday, adjusting
d = d.minusDays(7 - diff);
}
// find the n'th previous (considering that the first was already found above)
d = d.minusWeeks(n - 1);
return d;
}
Ниже приведены тесты для 04/06/2017
(воскресенье) За n=1
, это возвращает 04/06/2017
, и для n >= 2
он находит n-е предыдущее воскресенье с этой даты (учитывая, что 04/06/2017
сам первый)
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 4, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(2, new DateTime(2017, 6, 4, 10, 0))); // 2017-05-28
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 4, 10, 0))); // 2017-05-21
Тестирование для 05/06/2017
(не воскресенье), он получает те же результаты (как в первое предыдущее воскресенье 04/06/2017
):
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 5, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(2, new DateTime(2017, 6, 5, 10, 0))); // 2017-05-28
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 5, 10, 0))); // 2017-05-21
Тестирование на всю неделю до субботы (10/06/2017
):
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 6, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 7, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 8, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 9, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(1, new DateTime(2017, 6, 10, 10, 0))); // 2017-06-04
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 6, 10, 0))); // 2017-05-21
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 7, 10, 0))); // 2017-05-21
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 8, 10, 0))); // 2017-05-21
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 9, 10, 0))); // 2017-05-21
System.out.println(nPreviousSunday(3, new DateTime(2017, 6, 10, 10, 0))); // 2017-05-21
PS: я использую DateTime
, но вы также можете использовать этот код для org.joda.time.LocalDate
или org.joda.time.LocalDateTime
(алгоритм тот же, просто измените тип переменной в методе).
Java 8 подход (мои 2 цента)
В Java 8 вы можете использовать TemporalAdjuster
как уже ответили. Но просто чтобы поставить мои 2 цента, вы можете создать метод, который возвращает TemporalAdjuster
и затем вы можете использовать его с любым из типов времени Java:
// get the n'th previous dayOfWeek, from the given temporal
public TemporalAdjuster previous(int n, DayOfWeek dayOfWeek) {
return (temporal) -> {
// avoid zero or negative numbers (optional, see if it fits your use cases)
if (n <= 0) {
return temporal; // return the same temporal
}
// get first previous (or same) dayOfWeek
Temporal t = temporal.with(TemporalAdjusters.previousOrSame(dayOfWeek));
// find the n'th previous (considering that the first was already found above)
t = t.minus(n - 1, ChronoUnit.WEEKS);
return t;
};
}
Так что вы можете использовать это так:
System.out.println(LocalDate.of(2017, 6, 4).with(previous(1, DayOfWeek.SUNDAY))); // 2017-06-04
System.out.println(LocalDate.of(2017, 6, 4).with(previous(2, DayOfWeek.SUNDAY))); // 2017-05-28
System.out.println(LocalDate.of(2017, 6, 4).with(previous(3, DayOfWeek.SUNDAY))); // 2017-05-21
System.out.println(LocalDate.of(2017, 6, 5).with(previous(1, DayOfWeek.SUNDAY))); // 2017-06-04
System.out.println(LocalDate.of(2017, 6, 5).with(previous(2, DayOfWeek.SUNDAY))); // 2017-05-28
System.out.println(LocalDate.of(2017, 6, 5).with(previous(3, DayOfWeek.SUNDAY))); // 2017-05-21
Хорошо, что он также работает с другими типами:
LocalDateTime dt = LocalDateTime.of(2017, 6, 4, 10, 0);
System.out.println(dt.with(previous(1, DayOfWeek.SUNDAY))); // 2017-06-04T10:00
System.out.println(dt.with(previous(2, DayOfWeek.SUNDAY))); // 2017-05-28T10:00
ZonedDateTime zdt = ZonedDateTime.of(dt, ZoneId.of("America/Sao_Paulo"));
System.out.println(zdt.with(previous(1, DayOfWeek.SUNDAY))); // 2017-06-04T10:00-03:00[America/Sao_Paulo]
System.out.println(zdt.with(previous(2, DayOfWeek.SUNDAY))); // 2017-05-28T10:00-03:00[America/Sao_Paulo]
OffsetDateTime odt = OffsetDateTime.of(dt, ZoneOffset.ofHours(2));
System.out.println(odt.with(previous(1, DayOfWeek.SUNDAY))); // 2017-06-04T10:00+02:00
System.out.println(odt.with(previous(2, DayOfWeek.SUNDAY))); // 2017-05-28T10:00+02:00
Как previous()
метод возвращает TemporalAdjuster
Вам не нужно вызывать его каждый раз, просто сохраните регулятор в переменной и используйте его повторно:
TemporalAdjuster thirdPreviousSunday = previous(3, DayOfWeek.SUNDAY);
System.out.println(LocalDate.of(2017, 6, 4).with(thirdPreviousSunday)); // 2017-05-21
System.out.println(LocalDate.of(2017, 6, 5).with(thirdPreviousSunday)); // 2017-05-21
Еще одним преимуществом этого подхода является то, что код становится более разборчивым и чистым (IMO) и работает с любым днем недели.
PS: код ниже выдает исключение, если тип не имеет DayOfWeek
поле (как LocalTime
, который имеет только час / минуту / секунду / наносекунду):
// throws UnsupportedTemporalTypeException (because LocalTime doesn't have the DayOfWeek field)
LocalTime.now().with(previous(1, DayOfWeek.SUNDAY));
Напомню, что это уже происходит с существующими настройщиками:
// also throws exception (Unsupported field: DayOfWeek)
LocalTime.now().with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
Это имеет смысл, так как LocalTime
не имеет полей даты и не может знать о буднях.