Dates and Time Series
Time data has at least four independent concerns: instant, time zone, calendar frequency, and interval semantics. Make each one explicit.
Core types
Timestamp: one point in time.DatetimeIndex: an index of timestamps.Timedelta: an elapsed duration.Period: a calendar span such as a month or quarter.DateOffset: a calendar-aware movement such as month end or business day.
Parse at the boundary
events["occurred_at"] = pd.to_datetime(
events["occurred_at"],
format="%Y-%m-%dT%H:%M:%S%z",
errors="coerce",
utc=True,
)
An explicit format is faster to reason about and prevents locale ambiguity. Treat coerced parse failures as data-quality events and inspect them.
Time zones
tz_localizeattaches a zone to naive local clock readings.tz_convertconverts already-aware instants to another zone.
Store instants in UTC when possible and convert for presentation. Daylight-saving transitions create ambiguous or nonexistent local times; choose a policy rather than silently discarding the issue.
Index and resample
series = events.set_index("occurred_at")["value"].sort_index()
daily = series.resample("D").sum(min_count=1)
rolling_7d = series.rolling("7D").mean()
resample groups observations into calendar bins. rolling computes windows
around observations. Their parameters encode different questions; do not use one
as shorthand for the other.
For interval labels, specify boundary and labeling behavior (closed, label,
and origin where applicable) when defaults affect interpretation.
Calendar ranges and periods
month_ends = pd.date_range("2026-01-01", periods=6, freq="ME", tz="UTC")
quarters = month_ends.to_period("Q")
Use periods when the entity is a calendar span rather than a precise instant.