temporal

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 9, 2026 License: MIT Imports: 3 Imported by: 0

README

temporal

CI CodeQL Coverage Mutation Documentation Go Reference Release Go License

temporal is immutable temporal algebra for Go. It provides bounded instant and civil-date periods, explicit endpoint bounds, Allen relations, normalized period sets, fixed elapsed durations, local times, circular daily intervals, strict notation, versioned scalar/set encoding, PostgreSQL range adapters, and bounded parsers and iterators.

The module is a Go-native successor to github.com/faustbrian/go-temporal. It preserves useful mathematics without copying mutable or PHP-specific APIs.

Install

go get github.com/faustbrian/go-temporal

The minimum supported toolchain is Go 1.26.6. Civil-date features use github.com/faustbrian/go-calendar; clocks and timers deliberately remain in clock.

Five-minute instant period

start := time.Date(2026, 7, 16, 9, 0, 0, 0, time.UTC)
period, err := instant.Range(start, start.Add(90*time.Minute))
if err != nil { log.Fatal(err) }

parts, err := period.SplitForward(30*time.Minute, temporal.Limits{Steps: 10})
if err != nil { log.Fatal(err) }

set, err := instant.NewSet(temporal.Limits{}, parts...)
if err != nil { log.Fatal(err) }

dayStart, err := instant.Snap(
    start, instant.Day, instant.Floor, time.UTC, timezone.Reject,
)
if err != nil { log.Fatal(err) }
fmt.Println(set.Includes(start), set.Len()) // true 1: normalization merged parts

instant.Range is closed-open. Use instant.New for another bound mode. Instant duration is elapsed duration; calendar months are never inferred.

Five-minute civil-date period

month, err := dateperiod.Month(2026, time.July)
if err != nil { log.Fatal(err) }

weeks, err := month.SplitDays(7, temporal.Limits{Steps: 10})
if err != nil { log.Fatal(err) }

helsinki, _ := time.LoadLocation("Europe/Helsinki")
instants, err := month.ToInstant(helsinki, timezone.Reject)
if err != nil { log.Fatal(err) }

Date arithmetic delegates to calendar. Conversion uses the next civil boundary as an exclusive instant end, so DST-short and DST-long days remain correct.

Five-minute daily interval

start, _ := timeofday.Parse("22:00", temporal.Limits{})
end, _ := timeofday.Parse("02:00", temporal.Limits{})
night, err := timeofday.Between(start, end, temporal.ClosedOpen)
if err != nil { log.Fatal(err) }

set, _ := timeofday.NewIntervalSet(temporal.Limits{}, night)
offHours, err := set.Complement()
if err != nil { log.Fatal(err) }

helsinki, _ := time.LoadLocation("Europe/Helsinki")
date := calendar.MustDate(2026, time.July, 16)
instantNight, err := night.ToInstant(date, helsinki, timezone.Reject)
if err != nil { log.Fatal(err) }

fmt.Println(night.Kind(), night.Duration(), offHours.Len(), instantNight.Start())
// Circular 4h0m0s 1

24:00 is a distinct end boundary. Equal endpoints must be constructed as Collapsed or FullDay; they are never guessed.

Packages

  • temporal: bounds, Allen relations, typed errors, and resource limits.
  • instant: bounded time.Time periods and normalized sets.
  • dateperiod: bounded calendar.Date periods and normalized sets.
  • timeofday: local times, fixed durations, circular intervals, and sets.
  • notation: strict ISO 8601, ISO 80000, and Bourbaki codecs.
  • adapters/postgres: loss-checked PostgreSQL range and multirange adapters.
  • adapters/wire: versioned format-neutral scalar and set documents.
  • adapters/validation: deterministic validation rules.
  • adapters/config: atomic config text wrappers.
  • temporaltest: exhaustive relation fixtures and set assertions.

The released postgres, temporalwire, temporalvalidation, and temporalconfig imports remain compatibility facades for the longer of 180 days after successor availability and two subsequent stable minor releases.

Compatibility status

The audited PHP source is pinned at 469603239dbe700739c29b4c532a90382b6cbedf. The complete behavior inventory has a machine-checked classification and evidence pointer for every non-chart public symbol. Deliberate divergences are in docs/compatibility.md and docs/migration.md. Generated compatibility evidence is stored under compat/fixtures.

Charting is intentionally unsupported in v1. There is no temporalchart package and no terminal/Gantt renderer in core. Every PHP chart type and option is inventoried, and the core period/set values preserve a future renderer seam. Full PHP-package compatibility is not claimed while this gap remains.

See the versioned Golib ecosystem index and package-family selection guidance for the shared design language this module follows.

Quality and local gates

make cohesion
make check
golib mutation --module .
golib docs check --module .
make -f verification/package.mk php-compat PHP_TEMPORAL_SOURCE=/path/to/php-temporal

See the documentation index and testing guide for the evidence model and docs/hardening.md for the current algebra audit. See SECURITY.md for hostile-input and disclosure guidance. The specification decision register separates standard-backed codec and PostgreSQL behavior from package API policy. This library does not implement the Temporal service protocol.

License

MIT. See LICENSE.

Documentation

Overview

Package temporal defines shared bounds, Allen relations, typed errors, and resource limits for bounded temporal algebra packages.

Example (DailyInterval)
package main

import (
	"fmt"
	"time"

	calendar "github.com/faustbrian/go-calendar"
	calendartz "github.com/faustbrian/go-calendar/timezone"

	temporal "github.com/faustbrian/go-temporal"
	"github.com/faustbrian/go-temporal/timeofday"
)

func main() {
	start, _ := timeofday.Parse("22:00", temporal.Limits{})
	end, _ := timeofday.Parse("02:00", temporal.Limits{})
	night, _ := timeofday.Between(start, end, temporal.ClosedOpen)
	set, _ := timeofday.NewIntervalSet(temporal.Limits{}, night)
	offHours, _ := set.Complement()
	date := calendar.MustDate(2026, time.July, 16)
	instants, _ := night.ToInstant(date, time.UTC, calendartz.Reject)
	duration, _ := instants.Duration()

	fmt.Println(night.Kind(), night.Duration(), offHours.Len(), duration)
}
Output:
Circular 4h0m0s 1 4h0m0s
Example (DatePeriod)
package main

import (
	"fmt"
	"time"

	calendartz "github.com/faustbrian/go-calendar/timezone"

	temporal "github.com/faustbrian/go-temporal"
	"github.com/faustbrian/go-temporal/dateperiod"
)

func main() {
	month, _ := dateperiod.Month(2026, time.July)
	weeks, _ := month.SplitDays(7, temporal.Limits{Steps: 10})
	location, _ := time.LoadLocation("Europe/Helsinki")
	instants, _ := month.ToInstant(location, calendartz.Reject)
	duration, _ := instants.Duration()

	fmt.Println(month.Start(), month.End(), len(weeks), duration.Hours())
}
Output:
2026-07-01 2026-07-31 5 744
Example (InstantPeriod)
package main

import (
	"fmt"
	"time"

	temporal "github.com/faustbrian/go-temporal"
	"github.com/faustbrian/go-temporal/instant"
)

func main() {
	start := time.Date(2026, time.July, 16, 9, 0, 0, 0, time.UTC)
	period, _ := instant.Range(start, start.Add(90*time.Minute))
	parts, _ := period.SplitForward(30*time.Minute, temporal.Limits{Steps: 10})
	set, _ := instant.NewSet(temporal.Limits{}, parts...)

	fmt.Println(period.Bounds(), set.Includes(start), set.Len())
}
Output:
[) true 1

Index

Examples

Constants

View Source
const (
	// HardMaxPeriods bounds the input and output of any set operation.
	HardMaxPeriods = 100_000
	// HardMaxSteps bounds iteration and splitting.
	HardMaxSteps = 1_000_000
)

Variables

View Source
var (
	// ErrBounds identifies an unknown or malformed bounds value.
	ErrBounds = errors.New("temporal: invalid bounds")
	// ErrLimit identifies a configured or operational resource limit violation.
	ErrLimit = errors.New("temporal: resource limit exceeded")
	// ErrReversed identifies an interval whose end precedes its start.
	ErrReversed = errors.New("temporal: reversed interval")
	// ErrEmpty identifies an operation that requires a non-empty interval.
	ErrEmpty = errors.New("temporal: empty interval")
	// ErrStep identifies a zero or negative iteration step.
	ErrStep = errors.New("temporal: invalid step")
	// ErrOverflow identifies arithmetic outside the supported representation.
	ErrOverflow = errors.New("temporal: arithmetic overflow")
	// ErrParse identifies malformed or unsupported notation.
	ErrParse = errors.New("temporal: parse error")
	// ErrPrecision identifies input whose fractional precision is unsupported.
	ErrPrecision = errors.New("temporal: precision exceeded")
	// ErrInvalidTime identifies an invalid local time-of-day value.
	ErrInvalidTime = errors.New("temporal: invalid local time")
	// ErrUnsupported identifies a conversion that cannot preserve semantics.
	ErrUnsupported = errors.New("temporal: unsupported operation")
)

Functions

This section is empty.

Types

type Bounds

type Bounds uint8

Bounds describes endpoint inclusion for a bounded interval.

const (
	// ClosedOpen includes the start and excludes the end. It is the zero-value
	// operational default.
	ClosedOpen Bounds = iota
	// Closed includes both endpoints.
	Closed
	// Open excludes both endpoints.
	Open
	// OpenClosed excludes the start and includes the end.
	OpenClosed
)

func AllBounds

func AllBounds() []Bounds

AllBounds returns the four supported modes in canonical order.

func (Bounds) ExcludeEnd

func (b Bounds) ExcludeEnd() Bounds

ExcludeEnd returns a bounds value with an excluded end.

func (Bounds) ExcludeStart

func (b Bounds) ExcludeStart() Bounds

ExcludeStart returns a bounds value with an excluded start.

func (Bounds) IncludeEnd

func (b Bounds) IncludeEnd() Bounds

IncludeEnd returns a bounds value with an included end.

func (Bounds) IncludeStart

func (b Bounds) IncludeStart() Bounds

IncludeStart returns a bounds value with an included start.

func (Bounds) Includes

func (b Bounds) Includes(side Side) (bool, error)

Includes reports whether the selected endpoint is included.

func (Bounds) IncludesEnd

func (b Bounds) IncludesEnd() bool

IncludesEnd reports whether the end endpoint is a member.

func (Bounds) IncludesStart

func (b Bounds) IncludesStart() bool

IncludesStart reports whether the start endpoint is a member.

func (Bounds) MarshalText

func (b Bounds) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (Bounds) String

func (b Bounds) String() string

String returns ISO 80000 bracket notation for the bounds.

func (*Bounds) UnmarshalText

func (b *Bounds) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

func (Bounds) Valid

func (b Bounds) Valid() bool

Valid reports whether b is one of the four supported modes.

func (Bounds) WithSide

func (b Bounds) WithSide(side Side, included bool) (Bounds, error)

WithSide returns bounds with the selected endpoint included or excluded.

type LimitError

type LimitError struct {
	Field string
	Value int
	Max   int
}

LimitError describes which resource limit was invalid or exceeded.

func (*LimitError) Error

func (e *LimitError) Error() string

Error implements error.

func (*LimitError) Unwrap

func (e *LimitError) Unwrap() error

Unwrap makes LimitError discoverable with errors.Is.

type Limits

type Limits struct {
	ParseBytes    int
	Precision     int
	ErrorBytes    int
	FormatBytes   int
	InputPeriods  int
	OutputPeriods int
	Steps         int
	ParserDepth   int
}

Limits controls allocation and work performed by variable-size operations. A zero field selects its safe default. Values cannot exceed hard limits.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits returns the package's bounded operational defaults.

func (Limits) Resolve

func (l Limits) Resolve() Limits

Resolve replaces zero fields with safe defaults.

func (Limits) Validate

func (l Limits) Validate() error

Validate rejects negative fields and values above package hard limits.

type Relation

type Relation uint8

Relation is one of Allen's thirteen relations for two non-empty intervals.

const (
	// RelationInvalid is the safe zero value and is not an Allen relation.
	RelationInvalid Relation = iota
	// Before ends strictly before the other interval starts.
	Before
	// Meets ends exactly where the other interval starts.
	Meets
	// Overlaps starts first and ends inside the other interval.
	Overlaps
	// Starts shares the start and ends first.
	Starts
	// During starts after and ends before the other interval.
	During
	// Finishes starts later and shares the end.
	Finishes
	// Equal shares both endpoints with the other interval.
	Equal
	// FinishedBy starts first and shares the end.
	FinishedBy
	// Contains starts before and ends after the other interval.
	Contains
	// StartedBy shares the start and ends after the other interval.
	StartedBy
	// OverlappedBy starts inside the other interval and ends later.
	OverlappedBy
	// MetBy starts exactly where the other interval ends.
	MetBy
	// After starts strictly after the other interval ends.
	After
)

func AllRelations

func AllRelations() []Relation

AllRelations returns all Allen relations in canonical order.

func (Relation) Converse

func (r Relation) Converse() Relation

Converse returns the same relation with the operands exchanged.

func (Relation) String

func (r Relation) String() string

String returns the canonical lower-case relation name.

func (Relation) Valid

func (r Relation) Valid() bool

Valid reports whether r is one of Allen's thirteen relations.

type Side

type Side uint8

Side identifies one endpoint of a bounded interval.

const (
	// Start is the lower or beginning endpoint.
	Start Side = iota + 1
	// End is the upper or ending endpoint.
	End
)

func (Side) String

func (s Side) String() string

String returns the canonical endpoint name.

func (Side) Valid

func (s Side) Valid() bool

Valid reports whether s identifies an interval endpoint.

Directories

Path Synopsis
adapters
config
Package temporalconfig provides atomic text wrappers for config and other configuration decoders that honor encoding.TextUnmarshaler.
Package temporalconfig provides atomic text wrappers for config and other configuration decoders that honor encoding.TextUnmarshaler.
postgres
Package temporalpostgres provides loss-checked PostgreSQL range and multirange mappings for temporal values.
Package temporalpostgres provides loss-checked PostgreSQL range and multirange mappings for temporal values.
validation
Package temporalvalidation adapts temporal values to validation's deterministic immutable validator contract.
Package temporalvalidation adapts temporal values to validation's deterministic immutable validator contract.
wire
Package temporalwire provides versioned, format-neutral documents for encoding temporal values through wire or the standard JSON package.
Package temporalwire provides versioned, format-neutral documents for encoding temporal values through wire or the standard JSON package.
Package dateperiod provides immutable bounded intervals over civil calendar dates.
Package dateperiod provides immutable bounded intervals over civil calendar dates.
Package instant provides immutable bounded intervals over time.Time instants.
Package instant provides immutable bounded intervals over time.Time instants.
internal
diagnostic
Package diagnostic provides bounded errors for hostile input boundaries.
Package diagnostic provides bounded errors for hostile input boundaries.
Package notation provides strict codecs for temporal interval notation.
Package notation provides strict codecs for temporal interval notation.
Package postgres provides retained PostgreSQL adapters.
Package postgres provides retained PostgreSQL adapters.
Package temporalconfig provides retained configuration adapters.
Package temporalconfig provides retained configuration adapters.
Package temporaltest provides canonical algebra fixtures and assertions for temporal package consumers.
Package temporaltest provides canonical algebra fixtures and assertions for temporal package consumers.
Package temporalvalidation provides retained validation adapters.
Package temporalvalidation provides retained validation adapters.
Package temporalwire provides retained wire adapters.
Package temporalwire provides retained wire adapters.
Package timeofday provides immutable date-independent local time values and circular daily interval algebra.
Package timeofday provides immutable date-independent local time values and circular daily interval algebra.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL