Как рассчитать дату интервала n дней в функциональном стиле? - PullRequest
0 голосов
/ 06 ноября 2018

Я пытаюсь вычислить интервал 'n' дней от начальной до конечной даты. В сигнатуре функции в качестве аргумента указываются start_date, end_date, interval, которые возвращают карту со списком начальных, конечных дней заданных интервалов.

Example: start_date:2018-01-01 , End_date : 2018-02-20 interval: 20

Ожидаемый результат:

2018-01-01, 2018-01-20 (20 дней)

2018-01-21, 2018-02-09 (20 дней)

2018-02-09, 2018-01-20 (остальные)

Я пытался писать в scala, но не чувствую, что это правильный функциональный стиль письма.

case class DateContainer(period: String, range: (LocalDate, LocalDate))

    def generateDates(startDate: String, endDate: String,interval:Int): Unit = {

      import java.time._
      var lstDDateContainer = List[DateContainer]()
      var start = LocalDate.parse(startDate)
      val end = LocalDate.parse(endDate)
      import java.time.temporal._
      var futureMonth = ChronoUnit.DAYS.addTo(start, interval)
      var i = 1
      while (end.isAfter(futureMonth)) {
        lstDDateContainer = DateContainer("P" + i, (start, futureMonth)):: lstDDateContainer
        start=futureMonth
        futureMonth = ChronoUnit.DAYS.addTo(futureMonth, interval)
        i += 1
      }
      lstDDateContainer= DateContainer("P" + i, (start, end))::lstDDateContainer
      lstDDateContainer.foreach(println)

    }

    generateDates("2018-01-01", "2018-02-20",20)

Может ли кто-нибудь помочь мне написать в функциональном стиле.

Ответы [ 4 ]

0 голосов
/ 06 ноября 2018

Используйте библиотеку java.time для генерации дат и Stream.iterate() для генерации последовательности интервалов.

import java.time.LocalDate

def generateDates( startDate   :LocalDate
                 , endDate     :LocalDate
                 , dayInterval :Int ) :Unit = {
  val intervals =
    Stream.iterate((startDate, startDate plusDays dayInterval-1)){
      case (_,lastDate) => 
        val nextDate = lastDate plusDays dayInterval
        (lastDate plusDays 1, if (nextDate isAfter endDate) endDate 
                              else nextDate)
    }.takeWhile(_._1 isBefore endDate)
  println(intervals.mkString("\n"))
}

использование:

generateDates(LocalDate.parse("2018-01-01"), LocalDate.parse("2018-02-20"), 20)

// (2018-01-01,2018-01-20)
// (2018-01-21,2018-02-09)
// (2018-02-10,2018-02-20)
0 голосов
/ 06 ноября 2018

Если вы открыты для использования Joda для манипуляций с датой и временем , вот что я использую

import org.joda.time.{DateTime, Days}

// given from & to dates, find no of days elapsed in between (integer)
def getDaysInBetween(from: DateTime, to: DateTime): Int = Days.daysBetween(from, to).getDays

def getDateSegments(from: DateTime, to: DateTime, interval: Int): Seq[(DateTime, DateTime)] = {
    // no of days between from & to dates
    val days: Int = DateTimeUtils.getDaysInBetween(from, to) + 1

    // no of segments (date ranges) between to & from dates
    val segments: Int = days / interval

    // (remaining) no of days in last range
    val remainder: Int = days % interval

    // last date-range
    val remainderRanges: Seq[(DateTime, DateTime)] =
      if (remainder != 0) from -> from.plusDays(remainder - 1) :: Nil
      else Nil

    // all (remaining) date-ranges + last date-range
    (0 until segments).map { segment: Int =>
      to.minusDays(segment * interval + interval - 1) -> to.minusDays(segment * interval)
    } ++ remainderRanges
  }
0 голосов
/ 06 ноября 2018

Я предлагаю решение, которое дает несколько иной результат, чем приведенный в вопросе, но его можно легко изменить, чтобы получить желаемый ответ:

//Preliminaries
val fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val startDate ="2018-01-01"
val endDate = "2018-02-21"
val interval = 20L   
val d1 = LocalDate.parse(startDate, fmt)
val d2 = LocalDate.parse(endDate, fmt)

//The main code
Stream.continually(interval)
  .scanLeft((d1, d1.minusDays(1), interval)) ((x,y) => {
    val finDate = x._2.plusDays(y)
    if(finDate.isAfter(d2))
      (x._2.plusDays(1), d2, ChronoUnit.DAYS.between(x._2, d2))
    else
      (x._2.plusDays(1), x._2.plusDays(y), y)
  }).takeWhile(d => d._3 > 0).drop(1).toList

Результат:

(2018-01-01,2018-01-20,20)
(2018-01-21,2018-02-09,20)
(2018-02-10,2018-02-21,12)

Идея состоит в том, чтобы просканировать 3-кортеж через поток interval и остановиться, когда не осталось больше дней.

0 голосов
/ 06 ноября 2018

Что-то вроде (не проверено):

def dates(startDate: LocalDate, endDate: LocalDate, dayInterval: Int): List[(LocalDate, LocalDate, Int)] = {

  if(startDate.isAfter(endDate)) {
    Nil
  }
  else {

    val nextStart = startDate.plusDays(dayInterval)

    if(nextStart.isAfter(startDate)) {
      List((startDate, endDate, ChronoUnit.DAYS.between(startDate, endDate)))
    }
    else {
      (startDate, nextStart, dayInterval) :: dates(nextStart, endDate, dayInterval)
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...