Skip to main content

Temporal keys

Every Event in pond-ts has exactly one temporal key — the value that places the event on the timeline. The key is the first column of a TimeSeries's schema and is what every transform indexes against.

Three shapes are available; which one you pick is a modeling choice that says how the event sits on the timeline.

Timeline00:0000:0500:1000:1500:2000:25Timeone instant00:05TimeRangean unlabeled span00:1000:15Intervala labeled span"label"00:2000:25
KeyShape on the timelineUse when
TimeOne instantThe event is a point measurement
TimeRangeAn unlabeled span between two tsThe event covers a duration but has no bucket identity
IntervalA labeled span (carries an index)The event is a bucket produced by aggregate or align

Time

A single instant on the timeline. The most common temporal key — sensor samples, request timings, individual ticks.

import { Time } from 'pond-ts';

new Time(1735689600000);
new Time(new Date('2025-01-01T00:00:00Z'));
new Time(Date.parse('2025-01-01T00:00:00Z'));

Constructor accepts number | Date. The library does not parse ISO strings — go through Date.parse(...) or new Date(...) explicitly when starting from a string. Internal representation is milliseconds since Unix epoch.

time.begin() and time.end() both return the same number — a Time has zero extent. This makes Time interchangeable with TimeRange or Interval for any operator that only cares about the begin or end of the key.

TimeRange

A span between two timestamps with no bucket identity attached.

import { TimeRange } from 'pond-ts';

new TimeRange({
start: Date.parse('2025-01-01T00:02:00Z'),
end: Date.parse('2025-01-01T00:04:00Z'),
});

Use a TimeRange key when the event covers a duration but doesn't need to round-trip through a bucket-style identifier — sessions, request durations, outage windows, scrolling user-interaction frames.

range.begin() and range.end() return the two endpoints. Pond-ts treats the range as half-open [begin, end) — an event "at" the end boundary does not belong to this range.

Interval

A labeled span. Identical extent semantics to TimeRange plus an index value that lets the bucket round-trip through JSON and stay stable across transforms.

import { Interval } from 'pond-ts';

new Interval({
value: 1735689600000,
start: 1735689600000,
end: 1735689660000,
});

You almost never construct Intervals directly. They are produced by aggregate(sequence, mapping) and align(sequence, ...) — the sequence's bucket boundaries become the start / end, and the bucket's start timestamp becomes the index.

The index is what survives the series.toJSON()TimeSeries.fromJSON() round-trip. A bucketed event keyed by Interval reads back with the same identity it had before serialization; a TimeRange-keyed event would not.

Picking a key

If the event is…Use
A reading at one momentTime
A duration-shaped observation (session, span, frame)TimeRange
A bucket from a SequenceInterval

You declare the key kind in the schema's first column:

const schema = [
{ name: 'time', kind: 'time' }, // Time-keyed events
{ name: 'duration', kind: 'timeRange' }, // TimeRange-keyed events
{ name: 'bucket', kind: 'interval' }, // Interval-keyed events
// ...value columns
] as const;

When you pass rows to new TimeSeries({ rows }), the library constructs the right key shape from the cell value:

  • kind: 'time' accepts number | Date | Time
  • kind: 'timeRange' accepts a [start, end] tuple, { start, end }, or TimeRange
  • kind: 'interval' accepts an [value, start, end] tuple, { value, start, end }, or Interval

Common operations

All three key shapes share the same minimal interface:

key.begin(); // number — the earliest instant in the key's extent
key.end(); // number — the latest instant in the key's extent
key.toJSON(); // serialise to the schema-typed JSON form

For Time, begin() === end(). Operators that compare events by position on the timeline (sorting, between, tail, rolling cutoffs) work on any of the three uniformly through begin() / end().

Where this shows up

  • Event constructionnew Event(key, data) accepts any of the three key shapes; the type system threads the schema's first-column kind through to the resulting Event.
  • Schema first-column declaration — the kind: 'time' | 'timeRange' | 'interval' field drives row construction. See Creating series.
  • Aggregation and alignmentaggregate(seq, mapping) and align(seq, …) always produce Interval-keyed events. See Aggregation and Alignment.
  • Temporal relations — operations like tail, between, within, overlapping, trim all work in terms of begin() / end(). See Temporal relations.
  • Sequences — a Sequence defines a grid of bucket boundaries that aggregate and align consume to produce Interval keys. See Sequences.