Aligning Timestamps to Fixed Time Buckets Using Python and SQL

In data processing workflows, it is often necessary to normalize timestamps to the begining of a specific time bucket. For instance, if you are aggregating metrics in 30-minute windows, a timestamp like 2024-03-26 14:25:59 should be normalized to 2024-03-26 14:00:00. This article demonstrates how to achieve this floor-operation in Python using Pandas, as well as in ClickHouse and PostgreSQL.

Python Implementation with Pandas

The following approach uses a custom function to calculate the floored time. It determines the current minute, divides it by the interval size, and reconstructs the datetime object.

import pandas as pd

# Sample dataset
records = pd.DataFrame({
    'event_time': ['2024-03-26 07:00:00', '2024-03-26 07:15:00', '2024-03-26 07:29:59', '2024-03-26 07:50:00'],
    'metric': [10, 20, 30, 40]
})

def floor_to_interval(timestamp, span=30):
    """
    Rounds a datetime down to the nearest interval (e.g., 30 minutes).
    
    :param timestamp: pandas.Timestamp object
    :param span: The minute interval size (e.g., 5, 10, 30)
    :return: Normalized pandas.Timestamp
    """
    bucket_minute = (timestamp.minute // span) * span
    return timestamp.replace(minute=bucket_minute, second=0, microsecond=0)

# Convert column to datetime
records['event_time'] = pd.to_datetime(records['event_time'])

# Apply the normalization function
records['time_bucket'] = records['event_time'].apply(lambda t: floor_to_interval(t, span=30))
records

SQL Implementations

ClickHouse

ClickHouse provides a native function toStartOfInterval which simplifies this task significantly. You can specify the interval direct within the function call.

-- Normalizing a specific timestamp to a 30-minute window
SELECT toStartOfInterval(toDateTime('2024-02-10 12:13:49'), INTERVAL 30 MINUTE) AS window_start;

-- Applying to a table column
SELECT 
    event_time,
    toStartOfInterval(event_time, INTERVAL 30 MINUTE) AS window_start
FROM events_table;

PostgreSQL

PostgreSQL does not have a direct equivalent to ClickHouse's function, but you can combine date_trunc with itnerval arithmetic to achieve the same result. The strategy is to truncate to the hour and then add the floored minute difference.

-- Normalize '2024-12-12 12:35:31' to the start of a 30-minute interval
SELECT 
    timestamp '2024-12-12 12:35:31' AS original_time,
    date_trunc('hour', timestamp '2024-12-12 12:35:31') + 
    INTERVAL '30 min' * (EXTRACT(MINUTE FROM timestamp '2024-12-12 12:35:31')::int / 30) AS window_start;

Tags: python Pandas ClickHouse PostgreSQL sql

Posted on Sat, 03 Oct 2026 16:23:15 +0000 by slpctrl