tempo provides a formal representation of intervals between two points in time (periods) and the logical relations between them.
Intervals are a foundational concept in chronological modelling
across archaeology and other fields. Although R has several ways to
represent spans of time (e.g. Date and
POSIXct), these are based on the Gregorian calendar and are
unsuited to non-Gregorian or deep-time applications. tempo instead
represents intervals as vectors of start and end points on arbitrary
time scales. It is based on vctrs,
so the resulting S3 class is stable in data frames and tibbles, prints
in a readable format, and behaves predictably in tidyverse workflows.
Optionally, the calendar era of year-based time scales can be explicitly
specified via the era
package, providing calendar-aware chronological operations.
Logical relations between temporal intervals were first studied by Allen (1983), with later archaeological adaptations by Holst (2001), Holst (2004), and the CIDOC-CRM standard (ISO 21127 2014). They include relations like “x is before y”, “x meets y”, or “x overlaps y”. The package implements the typology developed by Levy et al. (2021) and Levy (2025), which is a superset of these previous typologies.
This vignette introduces the main features of the package: constructing and inspecting temporal intervals, performing set operations on them, and testing the logical relations between them.
Temporal intervals
The interval() function creates vectors of temporal
intervals from numeric start (earliest) and end (latest) points:
To specify the calendar eras, pass an era label via the
era argument:
interval(1200, 800, "BCE")
#> <interval[1]>
#> [1] 1200–800 BCEOr use era::yr() vectors directly:
interval(era::yr(c(100, 200), "BP"), era::yr(c(50, 100), "BP"))
#> <interval[2]>
#> [1] 100–50 BP 200–100 BPSee the era package vignette for details on working with calendar eras.
Making the era explicitly is especially useful for backwards counted
like BC(E) or Before Present, allowing for chronologically-aware
arithmetic. For example, intv_duration() takes into account
the counting direction when calculating the length of each interval:
x <- interval(1200, 800, "BCE")
intv_duration(x)
#> [1] 400Set operations
Two or more intervals can be combined using set operations.
intv_union() returns the bounding interval across all
inputs:
a <- interval(10, 30)
b <- interval(20, 40)
intv_union(a, b)
#> <interval[1]>
#> [1] 10–40intv_intersection() returns the overlapping region:
intv_intersection(a, b)
#> <interval[1]>
#> [1] 20–30Temporal relations
A temporal relation is a mathematical object describing the relationship between two temporal intervals, defined as a formal function of the intervals’ four endpoints (the start and end of each). They avoid the ambiguities of natural language descriptions — for example, statements about whether two phases were “contemporary” can be read in several different ways, whereas a formal relation has a single, precise meaning. In chronological modelling they serve as an exact vocabulary, enabling computational analysis and consistent comparison of chronological claims across studies. The remainder of this section introduces the typology of such relations implemented in tempo Levy et al. (2021); Levy (2025).
All relation functions share the signature
fn(x, y, strict = FALSE) and accept interval
objects, two-element numeric vectors, or lists of two-element numeric
vectors.
By default, comparisons are inclusive (using
>= and <=). Setting
strict = TRUE uses strict comparisons
(using > and <), which affects relations
that involve equality of endpoints. For example, two intervals sharing
an endpoint are contemporary_with() each other by default,
but not under strict comparison:
intv1 <- interval(1500, 1900)
intv2 <- interval(1800, 1950)
intv3 <- interval(1900, 1950)
contemporary_with(intv1, intv2)
#> [1] TRUE
# Inclusive (default): intervals sharing an endpoint are contemporary
contemporary_with(intv1, intv3)
#> [1] TRUE
# Strict: intervals must overlap in their interiors
contemporary_with(intv1, intv3, strict = TRUE)
#> [1] FALSEThe package provides 24 functions for testing temporal relations, listed in the table below in the order of Levy’s typology.
| Type | Relation | tempo function | Definition |
|---|---|---|---|
| Start–end order | Starts before or at end of | starts_before_end_of() |
beg(x) ≤ end(y) |
| Start–end order | Ends after or at start of | ends_after_start_of() |
end(x) ≥ beg(y) |
| Start order | Starts before or at start of | starts_before_start_of() |
beg(x) ≤ beg(y) |
| Start order | Starts after or at start of | starts_after_start_of() |
beg(x) ≥ beg(y) |
| End order | Ends before or at end of | ends_before_end_of() |
end(x) ≤ end(y) |
| End order | Ends after or at end of | ends_after_end_of() |
end(x) ≥ end(y) |
| Disjunction | Ends before or at start of | ends_before_start_of() |
end(x) ≤ beg(y) |
| Disjunction | Starts after or at end of | starts_after_end_of() |
beg(x) ≥ end(y) |
| Sequence | Meets | meets() |
end(x) = beg(y) |
| Sequence | Met by | met_by() |
beg(x) = end(y) |
| Contemporaneity | Contemporary with | contemporary_with() |
end(x) ≥ beg(y) AND beg(x) ≤ end(y) |
| Start inclusion | Starts during | starts_during() |
beg(y) ≤ beg(x) ≤ end(y) |
| Start inclusion | Includes start of | includes_start_of() |
beg(x) ≤ beg(y) ≤ end(x) |
| End inclusion | Ends during | ends_during() |
beg(y) ≤ end(x) ≤ end(y) |
| End inclusion | Includes end of | includes_end_of() |
beg(x) ≤ end(y) ≤ end(x) |
| Equal start | Starts with | starts_with() |
beg(x) = beg(y) |
| Equal end | Ends with | ends_with() |
end(x) = end(y) |
| Overlap | Overlaps before | overlaps_before() |
beg(x) ≤ beg(y) ≤ end(x) ≤ end(y) |
| Overlap | Overlaps after | overlaps_after() |
beg(y) ≤ beg(x) ≤ end(y) ≤ end(x) |
| Inclusion | Includes | includes() |
beg(x) ≤ beg(y) AND end(x) ≥ end(y) |
| Inclusion | Included in | included_in() |
beg(x) ≥ beg(y) AND end(x) ≤ end(y) |
| Beginning | Begins | begins() |
beg(x) = beg(y) AND end(x) ≤ end(y) |
| Beginning | Begun by | begun_by() |
beg(x) = beg(y) AND end(x) ≥ end(y) |
| Ending | Ends | ends() |
end(x) = end(y) AND beg(x) ≥ beg(y) |
| Ending | Ended by | ended_by() |
end(x) = end(y) AND beg(x) ≤ beg(y) |
| Equality | Equals | equal_to() |
beg(x) = beg(y) AND end(x) = end(y) |
Adapted from (2025, Table 5). The
definitions above are for the inclusive variants; set
strict = TRUE for the exclusive variants.