ergon_temporal_period (ergon v0.5.0)

View Source

The decoded form of a PostgreSQL temporal validity period.

PostgreSQL 19 has no scalar PERIOD type: application-time (valid_period), uniqueness (dedup_period), and system-time (system_time) are all tstzrange columns. from_pg_range/1 converts what the driver decodes such a column into, so callers work with a named shape instead of a positional tuple.

An infinite endpoint is unbounded; an empty range is empty := true with empty endpoints. Both matter to Ergon specifically: a job is live exactly when its valid_period upper bound is unbounded, and a non-unique job is one whose dedup_period is empty, because empty ranges never overlap and so never trip the uniqueness constraint.

Where this is and is not needed

No statement in priv/queries/ projects a range column today; every job-returning query selects scalars. This module is for hosts that query valid_period or system_time directly, and for the bi-temporal tests. It is pure, so nothing here needs a database.

Summary

Functions

Whether Instant falls within the period, honouring bound inclusivity and unbounded endpoints. An empty period contains nothing.

The empty period. Contains nothing and overlaps nothing.

Convert a range as the driver decodes it into a period.

A closed-open period [Lower, Upper), the bound style PostgreSQL uses for tstzrange. Either endpoint may be unbounded.

Types

period_endpoint()

-type period_endpoint() :: pg_timestamp() | unbounded | empty.

pg_range()

-type pg_range() ::
          empty | {{pg_timestamp() | unbound, pg_timestamp() | unbound}, {boolean(), boolean()}}.

pg_timestamp()

-type pg_timestamp() :: {calendar:date(), {0..23, 0..59, number()}} | infinity | '-infinity'.

temporal_period()

-type temporal_period() ::
          #{lower := period_endpoint(),
            upper := period_endpoint(),
            lower_inclusive := boolean(),
            upper_inclusive := boolean(),
            empty := boolean()}.

Functions

contains/2

-spec contains(temporal_period(), pg_timestamp()) -> boolean().

Whether Instant falls within the period, honouring bound inclusivity and unbounded endpoints. An empty period contains nothing.

empty()

-spec empty() -> temporal_period().

The empty period. Contains nothing and overlaps nothing.

from_pg_range/1

-spec from_pg_range(pg_range()) -> temporal_period().

Convert a range as the driver decodes it into a period.

pg_range is generic over its base type and answers either empty or {{From, To}, {LowerInclusive, UpperInclusive}}, using unbound for an infinite endpoint. This is the whole of the conversion: because that decoder is generic, Ergon needs no hand-written binary codec for tstzrange.

new(Lower, Upper)

A closed-open period [Lower, Upper), the bound style PostgreSQL uses for tstzrange. Either endpoint may be unbounded.