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:00Remember
- Parse with
pd.to_datetime(format=..., errors='coerce') .dtgives 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.