Skip to main content

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_localize attaches a zone to naive local clock readings.
  • tz_convert converts 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.

Source