3

How do we calculate previous Sunday or previous to previous Sunday or in general lets say how to find out sunday prior to n weeks? The catch is if today is Sunday then it should return today as Sunday and not last week.

Looking for Joda-Time or Java 8 time solution.

Edit: I tried

DateTime sunday = now
    .minusWeeks(1)
    .withDayOfWeek(DateTimeConstants.SUNDAY)
    .wi‌​thTimeAtStartOfDay()‌​;
DateTime previousWeekSunday = now
    .minusWeeks(2)
    .withDayOfWeek(DateTimeConstants.SATURDAY)
    .‌​withTime(23, 59, 59, 999); 

but if current is Sunday then this logic fails as it does not give today's date.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
java brain
  • 63
  • 1
  • 8
  • 3
    I suggest to start with reading the tutorial at https://docs.oracle.com/javase/tutorial/datetime/ and then showing the attempt from your end. – Pallavi Sonal Jun 02 '17 at 05:42
  • 1
    Seems you should prefer Java 8 over Joda-Time. [The Joda-Time homepage](http://www.joda.org/joda-time/) says: “Users are now asked to migrate to java.time (JSR-310).” – Ole V.V. Jun 02 '17 at 06:13
  • What did your search and research bring up? What have you tried already (and in what way did it fail)? – Ole V.V. Jun 02 '17 at 06:20
  • 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 current is sunday then this logic fails as it does not give today's date – java brain Jun 02 '17 at 06:27
  • Thanks for your attempt. I am sure a more Joda-Time knowledgeable girl or guy than me can say something clever about possible improvements. In any case, another time please edit your question and add new information there, not least when it’s code, but always. This time I did it for you. – Ole V.V. Jun 02 '17 at 06:58
  • 1
    thanks a lot for doing that on my behalf – java brain Jun 02 '17 at 07:14

2 Answers2

4

You basically need to check if today is Sunday, if not, then look back for the previous one...(or recursively move back the date if you need the previous one to that...)

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: previousOrSame method will skip the check of the actual dayof 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);
ΦXocę 웃 Пepeúpa ツ
  • 47,427
  • 17
  • 69
  • 97
  • 3
    `previousOrSame` of the `TemporalAdjuster` does the same thing in a single step. – Magnus Jun 02 '17 at 05:54
  • 1
    After you have found previous Sunday (or today if Sunday), you may use `prevSun.minusWeeks(n)` to count *n* Sundays backward. – Ole V.V. Jun 02 '17 at 06:12
  • 1
    A detail that nevertheless could make a difference: I prefer to pass an argument to `LocalDate.now()` to make time zone explicit. Even in the case where you pass `ZoneId.systemDefault()`. This way you are telling the reader — and yourself — that you have thought about time zone and decided which one you want. – Ole V.V. Jun 02 '17 at 07:02
  • 1
    You can probably remove the first code snippet from your answer. – assylias Jun 02 '17 at 07:23
1

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.