Skip to main content

Time-series functions

Core analytical functions for time-series computation. All are IMMUTABLE — safe for use in indexes, generated columns, and parallel queries.

time_bucket(p_interval, p_timestamp, p_origin)

The foundational time-series function. Truncates a timestamp to the nearest bucket boundary aligned to an origin point.

ParameterTypeDefaultDescription
p_intervalINTERVALBucket width (e.g., '1 hour', '5 minutes', '1 month')
p_timestampTIMESTAMPTZTimestamp to bucket
p_originTIMESTAMPTZ'2000-01-01 00:00:00+00'Alignment origin

Returns: TIMESTAMPTZ

Implementation details:

  • Sub-month intervals (seconds, minutes, hours, days, weeks): delegates to PostgreSQL's date_bin() for nanosecond precision
  • Month/year intervals: uses date_trunc + interval arithmetic to handle variable-length months
-- Hourly buckets
SELECT lakets.time_bucket('1 hour', time) AS bucket,
avg(temperature) AS avg_temp
FROM sensor_data
WHERE time >= now() - interval '7 days'
GROUP BY 1 ORDER BY 1;

-- 15-minute buckets aligned to epoch
SELECT lakets.time_bucket('15 minutes', time) AS bucket,
count(*) AS events
FROM events
GROUP BY 1;

-- Monthly aggregation
SELECT lakets.time_bucket('1 month', time) AS month,
sum(revenue) AS monthly_revenue
FROM transactions
GROUP BY 1 ORDER BY 1;

first(value, timestamp) — custom aggregate

Returns the value associated with the earliest timestamp in the group.

ParameterTypeDescription
valueDOUBLE PRECISIONThe value to return
timestampTIMESTAMPTZThe ordering timestamp

Returns: DOUBLE PRECISION

  • Internal state type: _first_last_state composite (value DOUBLE PRECISION, ts TIMESTAMPTZ)
  • Helper functions: _first_sfunc (state transition), _first_ffunc (final extraction)
-- Open / high / low / close per hour for a price feed
SELECT lakets.time_bucket('1 hour', time) AS bucket,
lakets.first(price, time) AS open_price,
lakets.last(price, time) AS close_price,
max(price) AS high,
min(price) AS low
FROM trades
GROUP BY 1;

last(value, timestamp) — custom aggregate

Returns the value associated with the latest timestamp in the group.

ParameterTypeDescription
valueDOUBLE PRECISIONThe value to return
timestampTIMESTAMPTZThe ordering timestamp

Returns: DOUBLE PRECISION

Internal state type: _first_last_state (shared with first).

time_bucket_gapfill(p_interval, p_start, p_finish)

Generates a continuous series of time buckets between start and finish. Use with LEFT JOIN to produce gap-filled results.

ParameterTypeDefaultDescription
p_intervalINTERVALBucket width
p_startTIMESTAMPTZSeries start (inclusive)
p_finishTIMESTAMPTZSeries end (inclusive — the bucket aligned to p_finish is included)

Returns: SETOF TIMESTAMPTZ

SELECT g.bucket, d.avg_temp
FROM lakets.time_bucket_gapfill('1 hour', '2026-04-01', '2026-04-02') AS g(bucket)
LEFT JOIN (
SELECT lakets.time_bucket('1 hour', time) AS bucket,
avg(temperature) AS avg_temp
FROM sensor_data
WHERE time >= '2026-04-01' AND time < '2026-04-02'
GROUP BY 1
) d ON d.bucket = g.bucket
ORDER BY g.bucket;

locf(p_value, p_prev_value)

Last Observation Carried Forward. Fills NULL values with the most recent non-NULL value. Use with window functions over gap-filled series.

ParameterTypeDefaultDescription
p_valueDOUBLE PRECISIONCurrent value (may be NULL)
p_prev_valueDOUBLE PRECISIONNULLPrevious non-NULL value from window

Returns: DOUBLE PRECISIONp_value if not NULL, else p_prev_value

SELECT bucket,
lakets.locf(
avg_temp,
lag(avg_temp) OVER (ORDER BY bucket)
) AS filled_temp
FROM gapfilled_data;

interpolate(p_value, p_prev_value, p_next_value, p_prev_time, p_curr_time, p_next_time)

Linear interpolation between two known data points. Computes value at p_curr_time proportional to its position between p_prev_time and p_next_time.

ParameterTypeDescription
p_valueDOUBLE PRECISIONCurrent value (returned as-is if not NULL)
p_prev_valueDOUBLE PRECISIONPrevious known value
p_next_valueDOUBLE PRECISIONNext known value
p_prev_timeTIMESTAMPTZTime of previous value
p_curr_timeTIMESTAMPTZTime to interpolate at
p_next_timeTIMESTAMPTZTime of next value

Returns: DOUBLE PRECISION

Formula: prev_value + (next_value - prev_value) * (curr_time - prev_time) / (next_time - prev_time)

SELECT bucket,
lakets.interpolate(
avg_temp,
lag(avg_temp) OVER w,
lead(avg_temp) OVER w,
lag(bucket) OVER w,
bucket,
lead(bucket) OVER w
) AS interpolated_temp
FROM gapfilled_data
WINDOW w AS (ORDER BY bucket);

delta(p_value, p_prev_value, p_handle_resets)

Difference between consecutive values. Handles counter resets (where value drops below previous — e.g. restarted process counters).

ParameterTypeDefaultDescription
p_valueDOUBLE PRECISIONCurrent value
p_prev_valueDOUBLE PRECISIONPrevious value
p_handle_resetsBOOLEANTRUETreat negative delta as counter reset (returns current value)

Returns: DOUBLE PRECISION

SELECT time,
lakets.delta(
bytes_sent,
lag(bytes_sent) OVER (ORDER BY time)
) AS bytes_delta
FROM network_stats;

rate(p_value, p_prev_value, p_time, p_prev_time, p_handle_resets)

Rate of change per second between consecutive points. Essential for monitoring counters.

ParameterTypeDefaultDescription
p_valueDOUBLE PRECISIONCurrent value
p_prev_valueDOUBLE PRECISIONPrevious value
p_timeTIMESTAMPTZCurrent timestamp
p_prev_timeTIMESTAMPTZPrevious timestamp
p_handle_resetsBOOLEANTRUEHandle counter resets

Returns: DOUBLE PRECISION — change per second

-- Requests per second from a monotonic counter
SELECT lakets.time_bucket('1 minute', time) AS bucket,
avg(lakets.rate(
request_count,
lag(request_count) OVER (PARTITION BY host ORDER BY time),
time,
lag(time) OVER (PARTITION BY host ORDER BY time)
)) AS rps
FROM http_metrics
GROUP BY 1;

histogram(p_value, p_min, p_max, p_num_buckets)

Returns a bucket index for frequency-distribution analysis.

ParameterTypeDescription
p_valueDOUBLE PRECISIONValue to classify
p_minDOUBLE PRECISIONHistogram lower bound
p_maxDOUBLE PRECISIONHistogram upper bound
p_num_bucketsINTNumber of equal-width buckets

Returns: INT — bucket index (0-based). NULL for NULL input. Out-of-range values are clamped: below p_min returns 0, at or above p_max returns p_num_buckets - 1.

-- Distribution of response times in 10 buckets between 0 and 1000ms
SELECT lakets.histogram(response_ms, 0, 1000, 10) AS bucket,
count(*) AS frequency
FROM requests
WHERE time >= now() - interval '1 hour'
GROUP BY 1 ORDER BY 1;