Andrei Markhel
Andrei Markhel

Reputation: 175

Date intervals keeping state

My task is to write function, that takes 2 LocalDateTime instances and generate list of intervals, splitted by 15 minutes

case class Interval(start:LocalDateTime, end:LocalDateTime)

so, for example if startDate = 2 Aug 2017 14:30:15 and endDate is 2 Aug 2017 15:00:00, intervals should be

List(Interval(2 Aug 2017 14:30:15, 2 Aug 2017 14:45:15), Interval(2 Aug 2017 14:45:15, 2 Aug 2017 15:00:00))

Here, is 2 main complications(at least for me) 1) If end date is less then prevDate + 15 min, then we need take min from(prevdate + 15 min, endDate) 2) We need somehow keep the state, because start of each interval is end of previous interval.

I am able to create imperative version, but I want to do it in functional style, please, help!)

x => {
  var start = x.startTime
  var end = min(findRightBorder(start), x.endTime)
  var result = ListBuffer(Interval(start, end))
  while (end.isBefore(x.endTime)) {
    start = end
    end = min(start.plusMinutes(QUARTER_MINUTES), x.endTime)
    result += Interval(start, end)
  }
  result.toList
}

private def findRightBorder(dt: LocalDateTime): LocalDateTime = {
  val minute = dt.getMinute
  if (minute >= 0 && minute < 15) dt.withMinute(15)
  else if (minute >= 15 && minute < 30) dt.withMinute(30)
  else if (minute >= 30 && minute < 45) dt.withMinute(45)
  else if (minute >= 45 && minute < 60) dt.withMinute(0).plusHours(1)
  else dt
}

private def min(dt1: LocalDateTime, dt2: LocalDateTime): LocalDateTime = {
  if (dt1.compareTo(dt2) < 0) dt1 else dt2
}

Upvotes: 0

Views: 70

Answers (2)

jwvh
jwvh

Reputation: 51271

Here's one way to go about this.

import java.time.LocalDateTime

case class Interval(start:LocalDateTime, end:LocalDateTime)

val dt1: LocalDateTime = ??? //some start DateTime
val dt2: LocalDateTime = ??? //some end DateTime

// a (potentially) infinite Stream of dates at 15 minute intervals
// starting at dt1 but ending before dt2
val dates = Stream.iterate(dt1)(_.plusMinutes(15L))
                  .takeWhile(_.isBefore(dt2))

val result = dates.sliding(2)  //pair the dates together
                  .toSeq       //a Seq is easier to append to
                  .map{case Seq(from,to) => Interval(from,to)}  //make Intervals
                  .:+(Interval(dates.last,dt2)) //append the final Interval
                  .toList      //if you need a List result

Upvotes: 1

Sergii Lagutin
Sergii Lagutin

Reputation: 10681

Another solution with streams:

x => {
  Stream
    .from(0)
    .map(i => x.startTime.plusMinutes(i * QUARTER_MINUTES))
    .takeWhile(_.isBefore(x.endTime))
    .map(s => Interval(s, min(s.plusMinutes(QUARTER_MINUTES), x.endTime)))
    .toList
}

Upvotes: 1

Related Questions