Iterate over dates range (the scala way)

2020-05-24 05:20发布

Given a start and an end date I would like to iterate on it by day using a foreach, map or similar function. Something like

(DateTime.now to DateTime.now + 5.day by 1.day).foreach(println)

I am using https://github.com/nscala-time/nscala-time, but I get returned a joda Interval object if I use the syntax above, which I suspect is also not a range of dates, but a sort of range of milliseconds.

EDIT: The question is obsolete. As advised on the joda homepage, if you are using java 8 you should start with or migrate to java.time.

7条回答
Evening l夕情丶
2楼-- · 2020-05-24 05:47

This answer fixes the issue of mrsrinivas answer, that .get(ChronoUnits.DAYS) returns only the days part of the duration, and not the total number of days.

Necessary import and initialization

import java.time.temporal.ChronoUnit
import java.time.{LocalDate, Period}

Note how above answer would lead to wrong result (total number of days is 117)

scala> Period.between(start, end)
res6: java.time.Period = P3M26D

scala> Period.between(start, end).get(ChronoUnit.DAYS)
res7: Long = 26

Iterate over specific dates between start and end

val start = LocalDate.of(2018, 1, 5)
val end   = LocalDate.of(2018, 5, 1)

// Create List of `LocalDate` for the period between start and end date

val dates: IndexedSeq[LocalDate] = (0L to (end.toEpochDay - start.toEpochDay))
  .map(days => start.plusDays(days))

dates.foreach(println)
查看更多
走好不送
3楼-- · 2020-05-24 05:57

You may use plusDays:

val now = DateTime.now
(0 until 5).map(now.plusDays(_)).foreach(println)

Given start and end dates:

import org.joda.time.Days

val start = DateTime.now.minusDays(5)
val end   = DateTime.now.plusDays(5)    

val daysCount = Days.daysBetween(start, end).getDays()
(0 until daysCount).map(start.plusDays(_)).foreach(println)
查看更多
对你真心纯属浪费
4楼-- · 2020-05-24 05:58

For just iterating by day, I do:

Iterator.iterate(start) { _ + 1.day }.takeWhile(_.isBefore(end))

This has proven to be useful enough that I have a small helper object to provide an implicit and allow for a type transformation:

object IntervalIterators {
  implicit class ImplicitIterator(val interval: Interval) extends AnyVal {
    def iterateBy(step: Period): Iterator[DateTime] = Iterator.iterate(interval.start) { _ + step }
        .takeWhile(_.isBefore(interval.end))

    def iterateBy[A](step: Period, transform: DateTime => A): Iterator[A] = iterateBy(step).map(transform)

    def iterateByDay: Iterator[LocalDate] = iterateBy(1.day, { _.toLocalDate })

    def iterateByHour: Iterator[DateTime] = iterateBy(1.hour)
  }
}

Sample usage:

import IntervalIterators._

(DateTime.now to 5.day.from(DateTime.now)).iterateByDay // Iterator[LocalDate]

(30.minutes.ago to 1.hour.from(DateTime.now)).iterateBy(1.second)  // Iterator[DateTime], broken down by second
查看更多
虎瘦雄心在
5楼-- · 2020-05-24 05:59

you can use something like that:

 object Test extends App {
   private val startDate: DateTime = DateTime.now()
   private val endDate: DateTime = DateTime.now().plusDays(5)
   private val interval: Interval = new Interval(startDate, endDate)
   Stream.from(0,1)
         .takeWhile(index => interval.contains(startDate.plusDays(index)))
         .foreach(index => println(startDate.plusDays(index)))
 }
查看更多
\"骚年 ilove
6楼-- · 2020-05-24 05:59

In this case, the Scala way is the Java way:

When running Scala on Java 9+, we can use java.time.LocalDate::datesUntil:

import java.time.LocalDate
import collection.JavaConverters._

// val start = LocalDate.of(2019, 1, 29)
// val end   = LocalDate.of(2018, 2,  2)
start.datesUntil(end).iterator.asScala
// Iterator[java.time.LocalDate] = <iterator> (2019-01-29, 2019-01-30, 2019-01-31, 2019-02-01)

And if the last date is to be included:

start.datesUntil(end.plusDays(1)).iterator.asScala
// 2019-01-29, 2019-01-30, 2019-01-31, 2019-02-01, 2019-02-02
查看更多
We Are One
7楼-- · 2020-05-24 06:03

Solution with java.time API using Scala

Necessary import and initialization

import java.time.temporal.ChronoUnit
import java.time.temporal.ChronoField.EPOCH_DAY
import java.time.{LocalDate, Period}

val now = LocalDate.now
val daysTill = 5

Create List of LocalDate for sample duration

(0 to daysTill)
  .map(days => now.plusDays(days))
  .foreach(println)

Iterate over specific dates between start and end using toEpochDay or getLong(ChronoField.EPOCH_DAY)

//Extract the duration
val endDay = now.plusDays(daysTill)
val startDay = now

val duration = endDay.getLong(EPOCH_DAY) - startDay.getLong(EPOCH_DAY)

/* This code does not give desired results as trudolf pointed
val duration = Period
  .between(now, now.plusDays(daysTill))
  .get(ChronoUnit.DAYS)
*/

//Create list for the duration
(0 to duration)
  .map(days => now.plusDays(days))
  .foreach(println)
查看更多
登录 后发表回答