# Temporal Operators and CTE Semantics

This document is the AI-facing reference for current Logline v1 temporal expressions.

## The core boundary

There are two different temporal operations in Logline:

1. **Automatic canonical timestamp normalization** happens before Engine execution. Each source record's top-level canonical timestamp is placed on the run's Canonical Timeline Epoch (CTE).
2. **Explicit payload-time interpretation** happens inside authored expressions. Timestamp-like values under `fields.*`, `signals.*`, `record.raw`, or other payload data are not rewritten automatically.

Do not describe these as one automatic mechanism.

## Exactness

Temporal arithmetic is exact. The runtime does not silently round an inexact conversion, average contradictory anchors, infer a zero offset, or guess a missing mapping.

Supported exact unit symbols are:

```text
nanoseconds
microseconds
milliseconds
seconds
minutes
hours
days
```

## `time.reference`

```logline
time.reference(source = "ecu-b", id = "device-clock")
```

Returns a typed **temporal-reference identity**. It identifies a named preflight-resolved clock/mapping. It does not itself return a timestamp and does not fall back to the source's canonical clock if the name is missing.

## `time.anchor`

```logline
time.anchor(source = "ecu-b", id = "boot-start")
```

Resolves the named anchor against the host-provided temporal bindings and returns its exact **CTE timestamp**. The anchor identity remains available in expression provenance.

## `time.toCte`

High-level form:

```logline
time.toCte(
  value,
  as = <temporal interpretation>,
  anchor = <optional CTE timestamp>,
  temporalReference = <optional temporal-reference identity>
)
```

### Displacement from a CTE anchor

```logline
time.toCte(
  fields.offsetMs,
  as = time.displacement(unit = milliseconds),
  anchor = record.timestamp
)
```

The value must convert exactly to nanoseconds and the anchor must already be a CTE timestamp.

### Named Unix/POSIX source clock

```logline
time.toCte(
  fields.deviceTime,
  as = time.epochTimestamp(epoch = unix, unit = milliseconds),
  temporalReference = time.reference(source = "ecu-b", id = "device-clock")
)
```

The named reference must resolve to a preflight mapping whose temporal domain is semantically compatible with the authored Unix/POSIX interpretation. Arithmetic convertibility alone is not enough.

## Interpretation constructors

Current temporal interpretation constructors are:

```text
time.displacement(...)
time.epochEncodedDisplacement(...)
time.epochTimestamp(...)
```

`time.epochEncodedDisplacement` currently requires `epoch = unix` and `format = iso8601`.

`time.epochTimestamp` currently supports `epoch = unix`, an explicit supported unit, optional `format = iso8601`, and optional timezone text subject to runtime/preflight rules.

## Absolute parsing and epoch construction

`time.parseTimestamp(text, ...)` parses exact ISO-8601 timestamps. Named timezone databases are not an Engine-local synchronization mechanism; runtime parsing directly supports UTC/Z semantics, while source-specific timezone/clock mapping belongs to preflight bindings.

`time.fromEpoch(value, epoch = unix, unit = ...)` constructs a CTE timestamp from an exact Unix epoch coordinate.

## Duration and timestamp operations

Current operators also include:

```text
time.duration
time.add
time.subtract
time.difference
time.absolute
time.before
time.after
time.within
time.between
time.floor
time.ceil
time.round
```

They have typed deterministic contracts. For example, `time.before` and `time.after` require CTE timestamps, and `time.within` requires two timestamps plus a duration.

## Missing bindings and diagnostics

The temporal runtime uses deterministic diagnostics including:

- `LGL_TIME_INVALID_REPRESENTATION`
- `LGL_TIME_INEXACT_CONVERSION`
- `LGL_TIME_MISSING_MAPPING`

A valid `time.reference(...)` identity can still fail when used if no resolved binding exists. `time.anchor(...)` resolves immediately and therefore fails at evaluation when the binding is absent.

## Static versus runtime-dependent binding identities

The compiler discovers `time.reference(...)` and `time.anchor(...)` use sites.

When both `source` and `id` are compiler-static, an Enterprise run treats that identity as an admission-time dependency. When either operand is computed from runtime data, admission does not guess; runtime lookup remains authoritative if the expression executes.

There is currently no optional/maybe-missing temporal binding operator. A compiler-static identity remains a dependency even if it appears under runtime control flow.

## What temporal operators do not do

Temporal operators do not:

- change the current record's canonical timestamp;
- reorder the canonical record stream;
- create synthetic records;
- synchronize sources at runtime;
- choose the run's CTE;
- invent missing source clocks, anchors, epochs, units, or timezones.
