Between
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.
Installation
Between is published for Scala 2.12 and 2.13. To start using it add the following to your build.sbt
:
resolvers += Resolver.bintrayRepo("gn0s1s", "releases")
libraryDependencies += "nl.gn0s1s" %% "between" % "0.4.2"
Example usage
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
Relations
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
Additional methods
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 pointcontains
, 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
Reasoning
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(<)
Resources
Allen's Interval Algebra:
- Maintaining Knowledge about Temporal Intervals
- Wikipedia entry
- Thomas A. Alspaugh's Foundations Material on Allen's Interval Algebra
Related links:
- A Modal Logic for Chopping Intervals
- SOWL QL: Querying Spatio - Temporal Ontologies in OWL
- AsterixDB Temporal Functions: Allen’s Relations
License
The code is available under the MIT license.