'Creating a dateRange Scala, Joda, Java

I have spent hours trying to make this next piece of code work.

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


def dateRange(from: DateTime, to: DateTime, step: Period): Iterator[DateTime]      =Iterator.iterate(from)(_.plus(step)).takeWhile(!_.isAfter(to))

val range = {
dateRange(new DateTime(2012, 06, 30).minusYears(5), new DateTime(2000, 06, 30),new Period.months(6))
}

I'm trying to set up a date range array that steps through from 2000 to 2012 in 6 month increments. The problem that I am facing is the following error.

Exception in thread "main" java.lang.IllegalArgumentException: No instant converter found for type: scala.Tuple3
at    org.joda.time.convert.ConverterManager.getInstantConverter(ConverterManager.java:165)
at org.joda.time.base.BaseDateTime.<init>(BaseDateTime.java:169)
at org.joda.time.DateTime.<init>(DateTime.java:241)
at tester.MomentumAlgo$class.$init$(MomentumAlgo.scala:154)
at tester.RunMomentumAlgo$$anon$1.<init>(RunMomentumAlgo.scala:86)
at tester.RunMomentumAlgo$.main(RunMomentumAlgo.scala:86)
at tester.RunMomentumAlgo.main(RunMomentumAlgo.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

I seems to be something to do with the last Period.months() part, however I have no idea how to fix it. The Tuple3 error I have no idea about.

If someone can give me a different solution, that would also be great. I want a list of dates from 2000 to 2012, every 6 months.

Any questions welcome. I thought this would be a common piece of code, but there isn't much on the net about it.

Thanks in advance.



Solution 1:[1]

A work around is to define the dates like this:

val date = new DateTime().withYear(2013).withMonthOfYear(7).withDayOfMonth(16)

The entire sequence in the REPL then becomes this:

scala> import org.joda.time.{DateTime, Period}
import org.joda.time.{DateTime, Period}

scala> def dateRange(from: DateTime, to: DateTime, step: Period): Iterator[DateTime]      =Iterator.iterate(from)(_.plus(step)).takeWhile(!_.isAfter(to))
dateRange: (from: org.joda.time.DateTime, to: org.joda.time.DateTime, step: org.joda.time.Period)Iterator[org.joda.time.DateTime]

scala> val from = new DateTime().withYear(2012).withMonthOfYear(6).withDayOfMonth(30).minusYears(5)
from: org.joda.time.DateTime = 2007-06-30T21:46:05.536-07:00

scala> val to = new DateTime().withYear(2000).withMonthOfYear(6).withDayOfMonth(30)
to: org.joda.time.DateTime = 2000-06-30T21:46:26.186-07:00

scala> val range = dateRange(from, to, new Period().withMonths(6))
range: Iterator[org.joda.time.DateTime] = non-empty iterator

scala> range.toList
res4: List[org.joda.time.DateTime] = List(
2000-06-30T21:46:26.186-07:00,
2000-12-30T21:46:26.186-08:00,
2001-06-30T21:46:26.186-07:00,
2001-12-30T21:46:26.186-08:00,
2002-06-30T21:46:26.186-07:00,
2002-12-30T21:46:26.186-08:00,
2003-06-30T21:46:26.186-07:00,
2003-12-30T21:46:26.186-08:00,
2004-06-30T21:46:26.186-07:00,
2004-12-30T21:46:26.186-08:00,
2005-06-30T21:46:26.186-07:00,
2005-12-30T21:46:26.186-08:00,
2006-06-30T21:46:26.186-07:00,
2006-12-30T21:46:26.186-08:00)

Also, I wasn't able to reproduce this as noted in my comment. Seems the behavior is different in the REPL and the compiler.

Solution 2:[2]

I was needing something similar. Here's what I came up with:

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

class DateRange(val start: DateTime, val end: DateTime, val step: Period, inclusive: Boolean) extends Iterable[DateTime] {
    override def iterator: Iterator[DateTime] = new DateRangeIterator

    class DateRangeIterator extends Iterator[DateTime] {
        var current = start

        override def hasNext: Boolean = current.isBefore(end) || (inclusive && current == end)

        override def next(): DateTime = {
            val returnVal = current
            current = current.withPeriodAdded(step, 1)
            returnVal
        }
    }
}

Example Usage:

val startOfDay: DateTime = new DateTime().withTimeAtStartOfDay()
val endOfDay: DateTime = startOfDay.plusDays(1)
val dateRange = new DateRange(startOfDay, endOfDay, Period.hours(1), false)
for (d <- dateRange) println(d)

Output:

2015-03-16T00:00:00.000-05:00
2015-03-16T01:00:00.000-05:00
2015-03-16T02:00:00.000-05:00
2015-03-16T03:00:00.000-05:00
2015-03-16T04:00:00.000-05:00
2015-03-16T05:00:00.000-05:00
2015-03-16T06:00:00.000-05:00
2015-03-16T07:00:00.000-05:00
2015-03-16T08:00:00.000-05:00
2015-03-16T09:00:00.000-05:00
2015-03-16T10:00:00.000-05:00
2015-03-16T11:00:00.000-05:00
2015-03-16T12:00:00.000-05:00
2015-03-16T13:00:00.000-05:00
2015-03-16T14:00:00.000-05:00
2015-03-16T15:00:00.000-05:00
2015-03-16T16:00:00.000-05:00
2015-03-16T17:00:00.000-05:00
2015-03-16T18:00:00.000-05:00
2015-03-16T19:00:00.000-05:00
2015-03-16T20:00:00.000-05:00
2015-03-16T21:00:00.000-05:00
2015-03-16T22:00:00.000-05:00
2015-03-16T23:00:00.000-05:00

Solution 3:[3]

Ok, Here is the complete working code.

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

object runme {

  def main(args:Array[String]) {

  def dateRange(from: DateTime, to: DateTime, step: Period): Iterator[DateTime]
  =Iterator.iterate(from)(_.plus(step)).takeWhile(!_.isAfter(to))

  val range = { dateRange(new DateTime(2000, 06, 30,0,0,0,0).minusYears(5) ,new DateTime(2013, 06, 30,0,0,0,0),new Period(0,6,0,0,0,0,0,0))}

  range.foreach(u => { 
    print(u.getYear)
    print(u.getMonthOfYear)
    println(u.getDayOfMonth)
  })

 }
}

I think my main problem was not having enough numbers after the DateTime() functions (ie the milliseconds etc.) this meant the compiler wasn't receiving all the parameters that it wanted. As mentioned by Alexey Romanov

This then prints the dates for a desired range, and can be used as an iterator.

Hope that helps others.

Thanks @Brian and others for the Help

Solution 4:[4]

DateTime doesn't have a constructor taking three int arguments, so new DateTime(2012, 06, 30) calls DateTime(Object) constructor with the tuple (2012, 06, 30) as the argument. The documentation says:

Constructs an instance from an Object that represents a datetime.

If the object implies a chronology (such as GregorianCalendar does), then that chronology will be used. Otherwise, ISO default is used. Thus if a GregorianCalendar is passed in, the chronology used will be GJ, but if a Date is passed in the chronology will be ISO.

The recognised object types are defined in ConverterManager and include ReadableInstant, String, Calendar and Date. The String formats are described by ISODateTimeFormat.dateTimeParser().

Unsurprisingly, ConverterManager doesn't know what to do with a Scala tuple, which results in the exception.

If someone can give me a different solution, that would also be great. I want a list of dates from 2000 to 2012, every 6 months.

If you actually want dates, the better type to use is LocalDate (which does have the constructor you want, by the way). If you want DateTime at the start of these dates, then you need to think about what time zone to use.

Solution 5:[5]

I had some small changes to the code of hucko

private def generateDateRange(from: LocalDate, to: LocalDate): List[LocalDate] = {
    if (from.compareTo(to) > 0) Nil
    else from :: generateDateRange(from.plusDays(1), to)
}

Solution 6:[6]

I came up with this simple solution:

def generateDateRange(from: LocalDate, to: LocalDate): List[Date] = {
    if(from.compareTo(to) > 0) Nil
    from :: generateDateRange(from.plusDays(1), to)
}

We recursively build a list of dates by prepending the start of the range to the new range which starts a day later.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Robert Beatty
Solution 2 Brandon Kearby
Solution 3 giampaolo
Solution 4 Community
Solution 5 Hans Adriaans
Solution 6 Nimantha