The question was already answered with a Java 8 approach, but just for the record, here's a Joda-Time's solution (and also my 2 cents for Java 8).
If I understood correctly, the general algorithm to get the nth previous Sunday is:
- if the current date is already a Sunday, go back
n-1
weeks (so for n=1
, it returns the same date)
- else, find the nth previous Sunday from that date
I've created a method that receives n
(the number of weeks) and a DateTime
(the date to start from). The code is:
// 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;
}
Below are the tests for 04/06/2017
(a Sunday). For n=1
, it returns 04/06/2017
, and for n >= 2
it finds the nth previous Sunday from that date (considering that 04/06/2017
itself is the first one):
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
Testing for 05/06/2017
(not a Sunday), it gets the same results (as the first previous Sunday is 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
Testing for the whole week until Saturday (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: I'm using a DateTime
, but you can also use this code for a org.joda.time.LocalDate
or a org.joda.time.LocalDateTime
(the algorithm is the same, just change the variable's type in the method).
Java 8 approach (my 2 cents)
In Java 8 you can use the TemporalAdjuster
as already answered. But just to put my 2 cents, you can create a method that returns a TemporalAdjuster
and then you can use it with any of the java-time types:
// 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;
};
}
So you could use it this way:
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
The good thing is that it also works with another types:
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
As the previous()
method returns a TemporalAdjuster
, you don't need to call it every time, just store the adjuster in a variable and reuse it:
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
Another advantages of this approach are: the code becomes more legible and clean (IMO), and it works with any day of the week.
PS: the code below throws an exception if the type doesn't have the DayOfWeek
field (like the LocalTime
, that has only hour/minute/second/nanosecond):
// throws UnsupportedTemporalTypeException (because LocalTime doesn't have the DayOfWeek field)
LocalTime.now().with(previous(1, DayOfWeek.SUNDAY));
Just reminding that it already happens with the existing adjusters:
// also throws exception (Unsupported field: DayOfWeek)
LocalTime.now().with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
It makes sense, as the LocalTime
has no date fields and can't know about weekdays.