datetime cheatsheet. Avoid naive datetimes in 2026.
Construct (aware = with timezone)
from datetime import datetime, date, time, timedelta, timezone
from zoneinfo import ZoneInfo
# Current
datetime.now(timezone.utc) # UTC, aware
datetime.now(ZoneInfo("America/New_York")) # zoned, aware
# WRONG: naive (no timezone)
datetime.now() # avoid
datetime.utcnow() # deprecated 3.12+
# Construct with tz
dt = datetime(2026, 5, 17, 12, 0, tzinfo=ZoneInfo("UTC"))
ISO 8601 (preferred string format)
# Parse
dt = datetime.fromisoformat("2026-05-17T12:00:00+00:00")
# Format
dt.isoformat() # "2026-05-17T12:00:00+00:00"
Always use ISO 8601 for transport.
Convert timezones
ny = datetime.now(ZoneInfo("America/New_York"))
in_utc = ny.astimezone(timezone.utc)
in_tokyo = ny.astimezone(ZoneInfo("Asia/Tokyo"))
astimezone converts; doesn’t change the instant.
strptime / strftime
# Parse custom format
dt = datetime.strptime("2026-05-17 12:00:00", "%Y-%m-%d %H:%M:%S")
# Naive! Add tz:
dt = dt.replace(tzinfo=timezone.utc)
# Format
dt.strftime("%Y-%m-%d %H:%M:%S %Z")
Common formats:
%Y year (2026)
%m month (01-12)
%d day (01-31)
%H hour (00-23)
%M minute
%S second
%f microseconds
%Z tz name
%z tz offset (+0000)
%A weekday full (Saturday)
%a weekday abbr (Sat)
%B month full
%b month abbr
%j day of year
%U week of year
Arithmetic
now = datetime.now(timezone.utc)
yesterday = now - timedelta(days=1)
next_week = now + timedelta(weeks=1)
in_2_hours = now + timedelta(hours=2)
# Difference
delta = future - past
delta.total_seconds()
delta.days
Comparing datetimes
a = datetime.now(timezone.utc)
b = datetime.now(timezone.utc)
a < b # works (both aware)
Can’t compare naive and aware datetimes — TypeError.
Convert to / from Unix timestamp
ts = dt.timestamp() # float seconds since epoch
dt = datetime.fromtimestamp(ts, tz=timezone.utc)
Date (no time component)
today = date.today()
date(2026, 5, 17)
Date is timezone-less.
Time (no date)
t = time(12, 30, 0)
Combine date + time + tz
dt = datetime.combine(date.today(), time(12, 0), tzinfo=ZoneInfo("UTC"))
Round to start of day
def start_of_day(dt):
return dt.replace(hour=0, minute=0, second=0, microsecond=0)
def end_of_day(dt):
return dt.replace(hour=23, minute=59, second=59, microsecond=999999)
Start / end of month
from calendar import monthrange
def start_of_month(dt):
return dt.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
def end_of_month(dt):
_, last = monthrange(dt.year, dt.month)
return dt.replace(day=last, hour=23, minute=59, second=59)
Subtract days respecting calendar
For “1 month ago”: tricky (different month lengths).
from dateutil.relativedelta import relativedelta
dt - relativedelta(months=1) # 1 month back, smart
dt - relativedelta(years=1)
Requires python-dateutil.
Parse natural language
from dateutil.parser import parse
parse("Aug 28, 2026") # datetime
parse("2026-05-17T12:00:00Z")
Lenient; useful for user input. For strict: fromisoformat.
Sleep / wait
import time
time.sleep(1) # blocks
import asyncio
await asyncio.sleep(1) # async
Time it
import time
t = time.perf_counter()
do_work()
duration = time.perf_counter() - t
perf_counter for measurements (monotonic; high precision).
Pendulum (alternative)
import pendulum
now = pendulum.now("UTC")
now.add(days=1)
now.subtract(months=1)
now.diff(other).in_hours()
Cleaner API; superset of datetime. Use if you prefer.
Common mistakes
- Naive datetime — DST, ambiguous, dangerous.
datetime.utcnow()— deprecated in 3.12+; usedatetime.now(timezone.utc).- Storing local times in DB — always UTC.
- Forgetting DST when adding hours — use
relativedeltafor human meaning. - Sorting mixed naive + aware — TypeError.
Read this next
If you want my datetime utilities library (parse, format, ranges), it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .