Kotlin: How to iterate all dates within a Joda Interval?
Heavily inspired by your current solution:
fun Interval.toDateTimes() = generateSequence(start) { it.plusDays(1) }
.takeWhile(::contains)
Usage:
interval.toDateTimes()
.forEach { println(it) }
If you need the LocalDate
you could still do the following instead:
interval.toDateTimes()
.map(DateTime::toLocalDate)
.forEach { println(it) }
or as an extension function to Interval
again:
fun Interval.toLocalDates() = toDateTimes().map(DateTime::toLocalDate)
If you want the end date to be inclusive instead, use takeWhile { it <= end }
instead.
The following extension function gives a Sequence
of LocalDate
objects from the given Interval
, which can be used to iterate those dates.
fun Interval.toLocalDates(): Sequence<LocalDate> = generateSequence(start) { d ->
d.plusDays(1).takeIf { it < end }
}.map(DateTime::toLocalDate)
Usage:
val interval = Interval(DateTime.now().minusDays(42), DateTime.now())
interval.toLocalDates().forEach {
println(it)
}
In this solution, the last day, DateTime.now()
is not included in the Sequence
since that's how Interval
is implemented as well:
"A time interval represents a period of time between two instants. Intervals are inclusive of the start instant and exclusive of the end."
If, for any reason, you want to make it include the last day, just change the takeIf
condition to it <= end
.
I guess if you need it more than once, it would be better to overload rangeTo operator to allow this syntax
for (i in LocalDate.now() .. LocalDate.now().plusWeeks(1)) {
System.out.print(i) // 2018-08-30 2018-08-31 2018-09-01
}
Here is the code for operator extension:
operator fun LocalDate.rangeTo(other: LocalDate): LocalDateRange {
return LocalDateRange(this, other)
}
And necessary classes:
class LocalDateRange(override val start: LocalDate, override val endInclusive: LocalDate)
: ClosedRange<LocalDate>, Iterable<LocalDate> {
override fun iterator(): Iterator<LocalDate> {
return DateIterator(start, endInclusive)
}
}
class DateIterator(start: LocalDate, private val endInclusive: LocalDate)
: Iterator<LocalDate> {
private var current = start
override fun hasNext(): Boolean {
return current <= endInclusive
}
override fun next(): LocalDate {
current = current.plusDays(1)
return current
}
}