Q&A library Python

How do I round timestamps to the nearest hour or day in pandas?

Python Intermediate datetimefloorrounding

Short answer

Use dt.floor to round down, dt.ceil to round up and dt.round to go to the nearest. Floor is usually what you want for bucketing, because round can push a timestamp into the next period.

The short version

dt.floor("h") rounds down to the hour, dt.ceil("h") rounds up, and dt.round("h") goes to whichever is nearer. For grouping events into time buckets, use floor, because round can push a timestamp forward into a period it did not belong to.

The three in action

import pandas as pd

t = pd.Series(pd.to_datetime(["2021-01-01 10:37"]))

t.dt.floor("h")   # 2021-01-01 10:00, rounded down
t.dt.round("h")   # 2021-01-01 11:00, nearest
t.dt.ceil("h")    # 2021-01-01 11:00, rounded up

Note that round sent 10:37 forward to 11:00, because it is nearer. If you were bucketing events by hour, that event would be counted in the 11 o'clock hour despite happening at 10:37, which is wrong.

Why floor is the right default for bucketing

An event at 10:37 happened during the ten o'clock hour. Flooring assigns it there, which is what "which hour did this happen in" means. Rounding assigns events to whichever hour is closer, which splits a single hour's events across two buckets and inflates one at the expense of the other.

So for grouping by time period, always floor.

df["hour"] = df["timestamp"].dt.floor("h")
df.groupby("hour").size()

Common frequencies

The same codes work throughout: "h" hour, "D" day, "min" minute, "15min" for a quarter-hour bucket, "s" second.

df["timestamp"].dt.floor("15min")   # quarter-hour buckets
df["timestamp"].dt.floor("D")       # midnight of the same day

Flooring to "D" is the standard way to strip the time and keep only the date, while remaining a real datetime you can sort and compare.

Just the date

If you want a plain date rather than a datetime at midnight, dt.date gives one, but note it produces Python date objects, which lose the fast datetime operations.

df["timestamp"].dt.date        # date objects, slower for further work
df["timestamp"].dt.floor("D")  # still a datetime, keeps dt functionality

Prefer floor("D") when you will do more date work, and dt.date only for final display.

Rounding and time zones

On an aware timestamp, flooring operates on the local wall-clock time, which is usually what you want ("which local hour did this fall in"). But around a clock change, local hours are not all one hour long, so a floored bucket can contain more or fewer minutes than you expect. If exact durations matter, floor in UTC and convert afterwards.

Rounding halves

dt.round uses the same round-half-to-even convention as Python's round, so a timestamp exactly on a half hour rounds to the even hour. This is rarely important, but it explains an occasional surprise where a timestamp rounds "the wrong way".

Need this solved on your own data?

The library is free and always will be. When the problem is bigger than an answer, we take on the work.

Start a project