# 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.13. To start using it add the following to your `build.sbt`

:

```
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`

and`after`

are also available as`precedes`

and`precededBy`

, respectively.`finishes`

and`finishedBy`

are also available as`ends`

and`endedBy`

.

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 interval`encloses`

, checks if the interval encloses the supplied interval`enclosedBy`

, checks if the interval is enclosed by the supplied interval`gap`

, returns the interval that is between this interval and the supplied interval`intersection`

, returns the intersection of this interval and the supplied interval`minus`

, returns the result of subtracting the supplied interval from this interval`span`

, returns the smallest interval that contains this interval and the supplied interval`union`

, returns the union of this interval and the supplied interval

Some point related methods are:

`after`

, checks if the interval is after the supplied point`before`

, checks if the interval is before the supplied point`chop`

, chops this interval into two intervals that meet at the supplied point`clamp`

, clamps a supplied point within the interval`contains`

, checks if supplied point is within the interval`endsAt`

, checks if the interval ends at the supplied point`startsAt`

, checks if the interval starts at the supplied point`with-`

, returns a copy of this interval with the supplied`-`

endpoint`with+`

, 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
- 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

## License

The code is available under the Mozilla Public License, version 2.0.