Dates and times basics

pandas · Practical · 15 min

Almost all business data is timestamped; correct parsing and date logic underpin every time-based analysis.

Convert text to real datetimes with `pd.to_datetime(s)` (or parse_dates= when reading). Pass format="%d/%m/%Y" when you know it: it's faster and avoids day/month ambiguity. errors="coerce" turns unparseable values into NaT (not-a-time) instead of raising. The resulting dtype is datetime64; pandas' scalar type is Timestamp.

The `.dt` accessor gives vectorized date parts and operations: dt.year, dt.month, dt.day_name(), dt.dayofweek (Monday=0), dt.quarter, dt.date, and rounding with dt.floor("D"), dt.to_period("M") for "month buckets".

Arithmetic: subtracting datetimes gives a Timedelta (dt.days, dt.total_seconds()); adding pd.Timedelta(days=7) shifts dates; pd.DateOffset(months=1) and offsets like pd.offsets.MonthEnd() respect calendar rules.

Ranges: pd.date_range("2024-01-01", periods=12, freq="MS") makes regular sequences. Frequency aliases: "D" day, "h" hour, "min" minute, "W" weekly, "MS"/"ME" month start/end, "QE" quarter end, "YE" year end. pandas 2.2 renamed several ("M" to "ME", "H" to "h"), so older code may show deprecation warnings.

DatetimeIndex: set a datetime column as the index (set_index("ts").sort_index()) and you can select with partial strings: df.loc["2024-03"] is all of March, df.loc["2024-03-01":"2024-03-15"] a range. That's also the basis for resampling and time-based rolling windows.

Time zones: datetimes are naive by default. dt.tz_localize("Europe/Oslo") attaches a zone, dt.tz_convert("UTC") converts. Store and compute in UTC; convert for display.

Example

import pandas as pd

df = pd.DataFrame({
    "ts": ["03/01/2024 08:15", "17/02/2024 21:40", "bad", "31/03/2024 06:05"],
    "amount": [20, 35, 10, 50],
})
df["ts"] = pd.to_datetime(df["ts"], format="%d/%m/%Y %H:%M", errors="coerce")
print(df["ts"].isna().sum())                  # 1  ('bad' -> NaT)

df["weekday"] = df["ts"].dt.day_name()
df["month"] = df["ts"].dt.to_period("M")
print(df[["weekday", "month"]].iloc[0].tolist())   # ['Wednesday', Period('2024-01', 'M')]

gap = df["ts"].iloc[1] - df["ts"].iloc[0]
print(gap.days)                               # 45

print(pd.date_range("2024-01-31", periods=3, freq="ME").strftime("%Y-%m-%d").tolist())
# ['2024-01-31', '2024-02-29', '2024-03-31']

s = df.dropna().set_index("ts").sort_index()["amount"]
print(s.loc["2024-02":"2024-03"].tolist())    # [35, 50]
utc = s.index.tz_localize("Europe/Oslo").tz_convert("UTC")
print(utc[0])                                 # 2024-01-03 07:15:00+00:00

Remember

  • Parse with pd.to_datetime(format=..., errors='coerce')
  • .dt gives vectorized date parts, rounding and periods
  • Datetime differences are Timedeltas; use offsets for calendar-aware shifts
  • A sorted DatetimeIndex supports partial-string slicing like loc['2024-03']

Watch out for

  • Parsing ambiguous dates like 03/01/2024 without format= and silently mixing up days and months.

Check yourself

  1. What does `pd.to_datetime(s, errors='coerce')` do with an unparseable value?
  2. With a sorted DatetimeIndex, what does `df.loc['2024-06']` select?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: pandas.pydata.org/docs/user_guide/timeseries.html