Micor Micor - 2 months ago 9
Groovy Question

Groovy way to find dates in range by the day of the week

What would be the Groovy way to accomplish Joda Time example below:

LocalDate startDate = new LocalDate(2016, 11, 8);
LocalDate endDate = new LocalDate(2017, 5, 1);

LocalDate thisMonday = startDate.withDayOfWeek(DateTimeConstants.MONDAY);

if (startDate.isAfter(thisMonday)) {
startDate = thisMonday.plusWeeks(1); // start on next monday
} else {
startDate = thisMonday; // start on this monday
}

while (startDate.isBefore(endDate)) {
System.out.println(startDate);
startDate = startDate.plusWeeks(1);
}


And what about by more than 1 day of the week: MONDAY and TUESDAY, for example

Answer

Given your start and end date parameters, the following Groovy code (using Groovy Date.parse() and Date.format() mix-in functions, Groovy Date + int addition, Groovy String to int coercion, and a bit of algebra) seems to do the trick:

Date startDate = Date.parse("yyyy-MM-dd","2016-11-08")
Date endDate = Date.parse("yyyy-MM-dd","2017-05-01")

Date mondayStart = startDate + 6 - ((5 + (startDate.format("u") as int)) % 7)

while (mondayStart < endDate) {
    println mondayStart
    mondayStart += 7
}

Having said that, the while loop at the bottom is sort of... "open-ended". You can get a more analytical equivalent using Groovy's Range literal syntax and List.step() mix-in function. The following replaces only the while loop above:

(mondayStart..<endDate).step(7) { stepDate ->
    println stepDate
}

Part 2: More than Monday

In order to work with other days of the week we need to replace the constant 5 in the mondayStart assignment with a suitable sub-expression based on the day of the week. Fortunately the following expression works quite nicely:

7 - Calendar."${weekDay.toUpperCase()}"

Putting the whole thing together and massaging a bit, it looks something like this:

def weekDaysInDateRange(Date startDate, Date endDate, String weekDay = "monday") {
    startDate = startDate.clearTime()
    endDate = endDate.clearTime()
    def offset = 7 - Calendar."${weekDay.toUpperCase()}"
    def startDOW = startDate.format("u") as int
    def weekDayStartDate = startDate + 6 - (offset + startDOW) % 7

    (weekDayStartDate..<endDate).step(7) { stepDate ->
        println (stepDate.format("yyyy-MM-dd"))
    }
}

With that function defined, the following test code:

def now = new Date()
def nextMonthIsh = new Date() + 30
println "$now --> $nextMonthIsh"
println "============================================================="

["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]
.each { weekDay ->
    println """
$weekDay
=========="""
    weekDaysInDateRange(now, nextMonthIsh, weekDay)
}

Gives the following result:

Mon Sep 19 09:29:24 CDT 2016 --> Wed Oct 19 09:29:24 CDT 2016
=============================================================

sunday
==========
2016-09-25
2016-10-02
2016-10-09
2016-10-16

monday
==========
2016-09-19
2016-09-26
2016-10-03
2016-10-10
2016-10-17

tuesday
==========
2016-09-20
2016-09-27
2016-10-04
2016-10-11
2016-10-18

wednesday
==========
2016-09-21
2016-09-28
2016-10-05
2016-10-12

thursday
==========
2016-09-22
2016-09-29
2016-10-06
2016-10-13

friday
==========
2016-09-23
2016-09-30
2016-10-07
2016-10-14

saturday
==========
2016-09-24
2016-10-01
2016-10-08
2016-10-15