scalacheck-datetime is a library for helping use datetime libraries with ScalaCheck
The motivation behind this library is to provide a simple, easy way to provide generated date and time instances that are useful to your own domain.
For SBT, you can add the dependency to your project’s build file:
resolvers += Resolver.sonatypeRepo("releases")
"com.47deg" %% "scalacheck-toolbox-datetime" % "0.3.1" % "test"
Please, visit the homepage for more information
To arbitrarily generate dates and times, you need to have the Arbitrary
in scope for your date/time class.
Assuming Joda Time:
import com.fortysevendeg.scalacheck.datetime.joda.ArbitraryJoda._
import org.joda.time.DateTime
import org.scalacheck.Prop.forAll
check {
forAll { dt: DateTime => (dt.getDayOfMonth >= 1 && dt.getDayOfMonth <= 31) == res0 }
}
For all of the examples given in this document, you can substitute jdk8
for joda
and vice-versa,
depending on which library you would like to generate instances for.
The infrastructure behind the generation of date/time instances for any given date/time library,
which may take ranges into account, is done using a fairly simple typeclass, which has the type signature
ScalaCheckDateTimeInfra[D, R]
. That is to say, as long as there is an implicit ScalaCheckDateTimeInfra
instance in scope for a given date/time type D
(such as Joda’s DateTime
) and a range type R
(such as Joda’s Period
), then the code will compile and be able to provide generated date/time instances.
As stated, currently there are two instances, ScalaCheckDateTimeInfra[DateTime, Period]
for Joda Time and
ScalaCheckDateTimeInfra[ZonedDateTime, Duration]
for Java SE 8’s Date and Time.
If you wish to restrict the precision of the generated instances, this library refers to that as granularity.
You can constrain the granularity to:
When a value is constrained, the time fields are set to zero, and the rest to the first day of the month, or day of the year. For example, if you constrain a field to be years, the generated instance will be midnight exactly, on the first day of January.
To constrain a generated type, you simply need to provide an import for the typeclass for your date/time and
range, and also an import for the granularity. As an example, this time using Java SE 8's java.time
package:
import java.time._
import com.fortysevendeg.scalacheck.datetime.jdk8.ArbitraryJdk8._
import com.fortysevendeg.scalacheck.datetime.jdk8.granularity.years
import org.scalacheck.Prop.forAll
check {
forAll { zdt: ZonedDateTime =>
(zdt.getMonth == Month.JANUARY) &&
(zdt.getDayOfMonth == res0) &&
(zdt.getHour == res1) &&
(zdt.getMinute == res2) &&
(zdt.getSecond == res3) &&
(zdt.getNano == res4)
}
}
You can generate date/time instances only within a certain range, using the genDateTimeWithinRange
in the
GenDateTime
class. The function takes two parameters, the date/time instances as a base from which to generate
new date/time instances, and a range for the generated instances.
If the range is positive, it will be in the future from the base date/time, negative in the past.
Showing this usage with Joda Time:
import org.joda.time._
import com.fortysevendeg.scalacheck.datetime.instances.joda._
import com.fortysevendeg.scalacheck.datetime.GenDateTime.genDateTimeWithinRange
import org.scalacheck.Prop.forAll
val from = new DateTime(2016, 1, 1, 0, 0)
val range = Period.years(1)
check {
forAll(genDateTimeWithinRange(from, range))(dt => dt.getYear == res0)
}
As you would expect, it is possible to use the granularity and range concepts together. This example should not show anything surprising by now:
import org.joda.time._
import com.fortysevendeg.scalacheck.datetime.instances.joda._
import com.fortysevendeg.scalacheck.datetime.GenDateTime.genDateTimeWithinRange
import com.fortysevendeg.scalacheck.datetime.joda.granularity.days
import org.scalacheck.Prop.forAll
val from = new DateTime(2016, 1, 1, 0, 0)
val range = Period.years(1)
check {
forAll(genDateTimeWithinRange(from, range)) { dt =>
(dt.getYear == res0) &&
(dt.getHourOfDay == res1) &&
(dt.getMinuteOfHour == res2) &&
(dt.getSecondOfMinute == res3) &&
(dt.getMillisOfSecond == res4)
}
}