Dates & Times (datetime)
Almost every real program eventually needs to deal with dates and times — logging when something happened, scheduling a task, calculating someone's age, or measuring how long an operation took. Python's built-in datetime module provides a small family of classes for exactly this, and understanding how they fit together will save you from a lot of subtle bugs later on.
The Core Classes
The datetime module exposes four classes that matter most day to day:
Class | Represents | Example |
|---|---|---|
| A calendar date (year, month, day) |
|
| A time of day, with no date attached |
|
| A combined date and time |
|
| A duration — the difference between two dates/times |
|
date and datetime both have convenient class methods for getting the current moment: date.today() and datetime.now().
from datetime import date, datetime today = date.today() now = datetime.now() print(today) print(now) print(now.year, now.month, now.day, now.hour, now.minute)
2024-03-15 2024-03-15 14:32:07.812345 2024 3 15 14 32
Notice that datetime.now() includes microsecond precision by default, while date objects only ever carry year/month/day.
Formatting Dates to Strings: `strftime`
To turn a date/datetime object into a human-readable string, use .strftime() ("string format time"), which takes a format string built from special placeholder codes.
Code | Meaning | Example |
|---|---|---|
| Four-digit year |
|
| Zero-padded month |
|
| Zero-padded day |
|
| Hour, 24-hour clock |
|
| Minute |
|
| Second |
|
| Full weekday name |
|
| Full month name |
|
| Abbreviated month name |
|
| AM/PM |
|
from datetime import datetime
now = datetime(2024, 3, 15, 14, 32, 7)
print(now.strftime("%Y-%m-%d"))
print(now.strftime("%A, %B %d, %Y"))
print(now.strftime("%H:%M:%S"))
print(now.strftime("%d/%m/%Y %I:%M %p"))2024-03-15 Friday, March 15, 2024 14:32:07 15/03/2024 02:32 PM
Parsing Strings into Dates: `strptime`
Going the other direction — turning a string into a datetime object — uses datetime.strptime() ("string parse time"). You give it the string and the exact format it should expect; the format codes are the same table as above.
from datetime import datetime
text = "2024-03-15 14:32:07"
parsed = datetime.strptime(text, "%Y-%m-%d %H:%M:%S")
print(parsed)
print(type(parsed))
birthday = datetime.strptime("March 15, 1998", "%B %d, %Y")
print(birthday)2024-03-15 14:32:07 <class 'datetime.datetime'> 1998-03-15 00:00:00
Date Arithmetic with `timedelta`
timedelta represents a span of time and can be added to or subtracted from a date/datetime, or produced by subtracting two dates from each other. This is how you compute things like "7 days from now" or "how many days until my deadline".
from datetime import date, timedelta today = date(2024, 3, 15) one_week_later = today + timedelta(days=7) three_days_earlier = today - timedelta(days=3) print(one_week_later) print(three_days_earlier) deadline = date(2024, 12, 31) remaining = deadline - today print(remaining) print(remaining.days)
2024-03-22 2024-03-12 291 days, 0:00:00 291
timedelta accepts days, seconds, microseconds, milliseconds, minutes, hours, and weeks as keyword arguments, and can be combined and compared just like numbers.
Naive vs Aware Datetimes
Every datetime object is either naive or aware of timezones. A naive datetime (the kind produced by datetime.now()) has no timezone information attached — it's just numbers, with no notion of UTC offset or daylight saving. An aware datetime carries a tzinfo attached, typically via the timezone class or a third-party library.
from datetime import datetime, timezone, timedelta naive = datetime.now() aware_utc = datetime.now(timezone.utc) ist = timezone(timedelta(hours=5, minutes=30)) aware_ist = datetime.now(ist) print(naive) print(aware_utc) print(aware_ist) print(naive.tzinfo, aware_utc.tzinfo)
2024-03-15 14:32:07.812345 2024-03-15 09:02:07.812345+00:00 2024-03-15 14:32:07.812345+05:30 None UTC
from datetime import datetime, timezone naive = datetime(2024, 3, 15, 12, 0, 0) aware = datetime(2024, 3, 15, 12, 0, 0, tzinfo=timezone.utc) print(naive - aware)
Traceback (most recent call last): ... TypeError: can't subtract offset-naive and offset-aware datetimes
Beyond the Standard Library
The built-in datetime module is powerful but its timezone API is verbose, and it has no built-in concept of timezone databases (you typically pair it with zoneinfo from the standard library, or pytz on older Python versions). For serious datetime handling in real projects, many developers reach for third-party libraries such as pendulum or arrow, which wrap datetime in a much friendlier API — easier parsing, human-readable durations ("3 hours ago"), and saner timezone conversions out of the box.
Quick Reference
date.today()anddatetime.now()get the current date/time (naive by default)..strftime(fmt)converts a date/datetime object to a formatted string.datetime.strptime(text, fmt)parses a string into a datetime object.timedeltarepresents a duration and supports+,-, and comparisons.Subtracting two dates or datetimes produces a
timedelta.Naive and aware datetimes cannot be compared or subtracted directly — pick one and be consistent.
pendulumandarroware popular third-party alternatives with friendlier APIs.