How do I find the nth previous Sunday with Java or Joda-time dates API?
How do we calculate the previous Sunday or the previous Sunday, or generally tell how to find out Sunday before n weeks? The catch, if today is Sunday, it should be back today Sunday, not last week.
Looking for a Joda-Time or Java 8 solution.
Edit: I tried
DateTime sunday = now
.minusWeeks(1)
.withDayOfWeek(DateTimeConstants.SUNDAY)
.withTimeAtStartOfDay();
DateTime previousWeekSunday = now
.minusWeeks(2)
.withDayOfWeek(DateTimeConstants.SATURDAY)
.withTime(23, 59, 59, 999);
but if the current is Sunday, then this logic fails as it does not give today the date.
source to share
You need to check if today is Sunday, and if not, then look back to the previous one ... (or recursively return the date if you need the previous one to ...)
Using java 8 you will need:
LocalDate date = LocalDate.now();
DayOfWeek todayAsDayOfWeek = date.getDayOfWeek();
LocalDate prevSun = todayAsDayOfWeek == DayOfWeek.SUNDAY ? date : date.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
System.out.println(prevSun);
Edit: the previousOrSame method will skip checking the actual day of the week
LocalDate date = LocalDate.now();
LocalDate prevSun = date.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
prevSun = date.with(TemporalAdjusters.previousOrSame(DayOfWeek.SUNDAY));
System.out.println(prevSun);
source to share
The question was already covered with a Java 8 approach, but for the record, there was a Joda-Time solution here (as well as my 2 cents for Java 8).
If I understood correctly, the general algorithm for getting the n th of the previous Sunday is:
- If today's date is already Sunday, return
n-1
weeks (son=1
it returns the same date for) - else, find n th the previous sunday from that date
I created a method that gets n
(number of weeks) and DateTime
(start date). Code:
// 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
(Sunday). For, n=1
it returns 04/06/2017
, and for n >= 2
finds n th the previous Sunday from that date (assuming itself 04/06/2017
is the first):
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 Sunday), it gets the same results (as the previous Sunday 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 use DateTime
, but you can also use this code for org.joda.time.LocalDate
or org.joda.time.LocalDateTime
(the algorithm is the same, just change the type of the variable in the method).
Java 8 approach (my 2 cents)
In Java 8 you can use TemporalAdjuster
as already answered . But just to fit 2 cents, you can create a method that returns 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 can use it like this:
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 works with other types as well:
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
When the method previous()
returns TemporalAdjuster
, you don't need to call it every time, just store the regulator 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
Other benefits of this approach are: the code is clearer and cleaner (IMO) and it works any day of the week.
PS: The code below throws an exception if the type has no field DayOfWeek
(for example LocalTime
, which only has an hour / minute / second / nanosecond):
// throws UnsupportedTemporalTypeException (because LocalTime doesn't have the DayOfWeek field)
LocalTime.now().with(previous(1, DayOfWeek.SUNDAY));
We remind you that this is already happening with existing regulators:
// also throws exception (Unsupported field: DayOfWeek)
LocalTime.now().with(TemporalAdjusters.previous(DayOfWeek.SUNDAY));
This makes sense as LocalTime
it has no date fields and cannot know about weekdays.