Between is a library for working with (time) intervals and the relations between them. It takes as a basis the thirteen relations of Allen's Interval Algebra. This is a system for reasoning about (temporal) intervals as described in the paper Maintaining Knowledge about Temporal Intervals.
Between is published for Scala 2.13 and Scala 3. To start using it add the following to your build.sbt
:
libraryDependencies += "nl.gn0s1s" %% "between" % "0.6.0"
When the endpoints of an interval are known, the Interval[T]
case class is available for testing all possible
relations between intervals. It needs two values -
and +
of type T
which reflect the (inclusive)
endpoints of an interval. For the type T
there needs to be an implicit Ordering
trait available. Additionally the
endpoint -
needs to be smaller than the endpoint +
.
The example below shows two examples for Double
and java.time.Instant
:
import nl.gn0s1s.between._
val i = Interval[Int](1, 2) // i: nl.gn0s1s.between.Interval[Int] = Interval(1,2)
val j = Interval[Int](2, 3) // j: nl.gn0s1s.between.Interval[Int] = Interval(2,3)
i meets j // res0: Boolean = true
j metBy i // res1: Boolean = true
val k = Interval[java.time.Instant](java.time.Instant.ofEpochSecond(1000L), java.time.Instant.ofEpochSecond(2000L))
// k: nl.gn0s1s.between.Interval[java.time.Instant] = Interval(1970-01-01T00:16:40Z,1970-01-01T00:33:20Z)
val l = Interval[java.time.Instant](java.time.Instant.ofEpochSecond(1500L), java.time.Instant.ofEpochSecond(2500L))
// l: nl.gn0s1s.between.Interval[java.time.Instant] = Interval(1970-01-01T00:25:00Z,1970-01-01T00:41:40Z)
k overlaps l // res2: Boolean = true
l overlappedBy k // res3: Boolean = true
Given two intervals there is always only one of the following thirteen defined relations true:
relation | symbol | inverse | inverse relation | diagram |
---|---|---|---|---|
x before y |
< |
> |
y after x |
xxx yyy |
x equals y |
== |
== |
y equals x |
xxx |
x meets y |
m |
mi |
y metBy x |
xxxyyy |
x overlaps y |
o |
oi |
y overlappedBy x |
xxx |
x during y |
d |
di |
y contains x |
xxx |
x starts y |
s |
si |
y startedBy x |
xxx |
x finishes y |
f |
fi |
y finishedBy x |
xxx |
before
andafter
are also available asprecedes
andprecededBy
, respectively.finishes
andfinishedBy
are also available asends
andendedBy
.
There's a findRelation
method which can be used to find out which relation exists between two intervals. The
Relation
has an inverse
method implemented, which gives the inverse of a relation.
import nl.gn0s1s.between._
val i = Interval[Int](1, 2) // i: nl.gn0s1s.between.Interval[Int] = Interval(1,2)
val j = Interval[Int](2, 3) // j: nl.gn0s1s.between.Interval[Int] = Interval(2,3)
val relationBetweenIAndJ = i.findRelation(j) // relationBetweenIAndJ: nl.gn0s1s.between.Relation = m
relationBetweenIAndJ.inverse // res0: nl.gn0s1s.between.Relation = mi
A number of additional methods are availabe on the Interval[T]
case class, some of which may be familiar for users of
the ThreeTen-Extra Interval class.
abuts
, checks if the interval abuts the supplied intervalencloses
, checks if the interval encloses the supplied intervalenclosedBy
, checks if the interval is enclosed by the supplied intervalgap
, returns the interval that is between this interval and the supplied intervalintersection
, returns the intersection of this interval and the supplied intervalminus
, returns the result of subtracting the supplied interval from this intervalspan
, returns the smallest interval that contains this interval and the supplied intervalunion
, returns the union of this interval and the supplied interval
Some point related methods are:
after
, checks if the interval is after the supplied pointbefore
, checks if the interval is before the supplied pointchop
, chops this interval into two intervals that meet at the supplied pointclamp
, clamps a supplied point within the intervalcontains
, checks if supplied point is within the intervalendsAt
, checks if the interval ends at the supplied pointstartsAt
, checks if the interval starts at the supplied pointwith-
, returns a copy of this interval with the supplied-
endpointwith+
, returns a copy of this interval with the supplied+
endpoint
I got inspired to write this library during
Eric Evans' talk at the
Domain-Driven Design Europe 2018 conference. I started writing it in the train on my way
back from the conference, this can be represented like this:
write lib <-(o)- - train - -(>, mi)-> DDD Europe - -(di)-> EE talk <-(d) - - inspired
Since the composition table of relations and the constraints
method are implemented we can find out what the possible
relations between write lib
and DDD Europe
are:
import nl.gn0s1s.between._
Relation.constraints(Set(o), Set(<, m)) // res0: Set[nl.gn0s1s.between.Relation] = Set(<)
Allen's Interval Algebra:
- Maintaining Knowledge about Temporal Intervals
- Wikipedia entry
- Thomas A. Alspaugh's Foundations Material on Allen's Interval Algebra
- Moments and Points in an Interval-Based Temporal Logic
Related links:
- A Modal Logic for Chopping Intervals
- SOWL QL: Querying Spatio - Temporal Ontologies in OWL
- AsterixDB Temporal Functions: Allen’s Relations
- Haskell package that does something similar for Haskell - https://github.com/novisci/interval-algebra
The code is available under the Mozilla Public License, version 2.0.