PythonDates & Times (datetime)

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

date

A calendar date (year, month, day)

date(2024, 3, 15)

time

A time of day, with no date attached

time(14, 30, 0)

datetime

A combined date and time

datetime(2024, 3, 15, 14, 30, 0)

timedelta

A duration — the difference between two dates/times

timedelta(days=7)

date and datetime both have convenient class methods for getting the current moment: date.today() and datetime.now().

Python
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

%Y

Four-digit year

2024

%m

Zero-padded month

03

%d

Zero-padded day

15

%H

Hour, 24-hour clock

14

%M

Minute

32

%S

Second

07

%A

Full weekday name

Friday

%B

Full month name

March

%b

Abbreviated month name

Mar

%p

AM/PM

PM

Python
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.

Python
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
Warning
`strptime()` raises a `ValueError` if the string doesn't match the format exactly (including separators and padding). Always validate or catch exceptions when parsing user-supplied date strings.
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".

Python
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.

Python
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
Warning
Mixing naive and aware datetimes is one of the most common `datetime` bugs. Comparing or subtracting a naive datetime from an aware one raises `TypeError: can't subtract offset-naive and offset-aware datetimes` — and if you store naive timestamps from servers in different timezones without normalizing them, comparisons can be silently wrong instead of raising anything at all. Decide early whether your application works in naive local time or aware UTC, and stay consistent throughout.

Python
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
Tip
A common convention for backend systems is to store and process everything internally as aware UTC datetimes (`datetime.now(timezone.utc)`), and only convert to a user's local timezone at the moment of display.
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() and datetime.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.

  • timedelta represents 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.

  • pendulum and arrow are popular third-party alternatives with friendlier APIs.

Note
Since Python 3.9, the standard library also includes `zoneinfo`, which gives you access to the IANA timezone database (e.g. `ZoneInfo("America/New_York")`) without needing a third-party package just for timezone names.