Beginner
Dates and times appear in almost every real-world Python project — logging when an event occurred, scheduling tasks, calculating how long something took, or displaying timestamps to users. Without proper tools, working with dates in code becomes a nightmare of string parsing, timezone confusion, and off-by-one-day errors. Python’s built-in datetime module solves all of this cleanly and consistently.
The datetime module is part of Python’s standard library — no installation required. It provides several classes: date for calendar dates, time for time-of-day values, datetime for combined date and time, timedelta for representing durations, and timezone for handling timezone offsets. Together these classes cover the vast majority of date/time tasks you’ll encounter.
In this tutorial, you’ll learn how to create and manipulate date and datetime objects, format dates for display and parse them from strings, do date arithmetic with timedelta, work with timezones, and apply everything in a practical project that calculates age and upcoming birthdays. By the end you’ll handle dates in Python with confidence.
Working with Dates: Quick Example
Here’s a quick working example that covers the most common operations — getting today’s date, formatting it, and calculating a future date:
# datetime_quick.py
from datetime import date, datetime, timedelta
# Today's date
today = date.today()
print("Today:", today)
# Current date and time
now = datetime.now()
print("Now:", now.strftime("%Y-%m-%d %H:%M:%S"))
# Date arithmetic: 30 days from now
future = today + timedelta(days=30)
print("30 days from now:", future)
# Days until end of year
end_of_year = date(today.year, 12, 31)
days_left = (end_of_year - today).days
print(f"Days until end of year: {days_left}")
Output:
Today: 2026-04-17
Now: 2026-04-17 09:23:45
30 days from now: 2026-05-17
Days until end of year: 258
The key insight here is that subtracting two date objects returns a timedelta object, and you access its .days attribute to get the integer count. The strftime method formats datetimes into readable strings — we’ll cover all the format codes later in this tutorial.
What Is the datetime Module?
The datetime module provides classes for working with dates and times in Python. Think of it as Python’s built-in calendar and clock library. Unlike Unix timestamps (which are just large integers), datetime objects are human-readable, support arithmetic, and can be converted to and from formatted strings.
Here’s how the main classes relate to each other:
| Class | What It Represents | Example |
|---|---|---|
date | A calendar date (year, month, day) | date(2026, 4, 17) |
time | A time of day (hour, minute, second, microsecond) | time(9, 30, 0) |
datetime | A specific moment in time (date + time combined) | datetime(2026, 4, 17, 9, 30) |
timedelta | A duration or difference between two moments | timedelta(days=7, hours=2) |
timezone | A fixed UTC offset for timezone-aware datetimes | timezone(timedelta(hours=5)) |
In most everyday code, you’ll use date, datetime, and timedelta most often. The timezone class becomes important when your application serves users in multiple regions or interacts with APIs that return UTC timestamps.
Creating Date and Datetime Objects
There are several ways to create date and datetime objects depending on whether you know the specific values or need the current moment.
Getting the Current Date and Time
The most common starting point is getting today’s date or the current datetime. Use date.today() for just the date, or datetime.now() for date plus time:
# create_dates.py
from datetime import date, datetime
# Just the date (no time component)
today = date.today()
print(f"date.today(): {today}")
print(f"Year: {today.year}, Month: {today.month}, Day: {today.day}")
# Date + time (uses system's local time)
now = datetime.now()
print(f"\ndatetime.now(): {now}")
print(f"Hour: {now.hour}, Minute: {now.minute}, Second: {now.second}")
print(f"Microsecond: {now.microsecond}")
# UTC time (timezone-naive but in UTC)
utc_now = datetime.utcnow()
print(f"\ndatetime.utcnow(): {utc_now}")
Output:
date.today(): 2026-04-17
Year: 2026, Month: 4, Day: 17
datetime.now(): 2026-04-17 09:23:45.123456
Hour: 9, Minute: 23, Second: 45
Microsecond: 123456
datetime.utcnow(): 2026-04-17 07:23:45.123456
Creating Specific Dates
When you need to represent a fixed date (a birthday, a deadline, a historical event), pass the year, month, and day directly to the constructor. The datetime constructor accepts the same arguments plus optional hour, minute, second, and microsecond:
# specific_dates.py
from datetime import date, datetime
# Create a specific date
python_release = date(1991, 2, 20) # Python's first public release
print(f"Python released: {python_release}")
# Create a specific datetime
meeting = datetime(2026, 5, 1, 14, 30, 0) # May 1, 2026 at 2:30 PM
print(f"Meeting scheduled: {meeting}")
# Access individual components
print(f"Meeting day of week (0=Mon): {meeting.weekday()}")
print(f"Meeting ISO weekday (1=Mon): {meeting.isoweekday()}")
Output:
Python released: 1991-02-20
Meeting scheduled: 2026-05-01 14:30:00
Meeting day of week (0=Mon): 3
Meeting ISO weekday (1=Mon): 4
The weekday() method returns 0 for Monday through 6 for Sunday. isoweekday() returns 1 for Monday through 7 for Sunday — which one you use depends on your preference and how the day numbering will appear in your output.
Date Arithmetic with timedelta
One of the most powerful features of the datetime module is the ability to add and subtract time using timedelta objects. A timedelta represents a fixed duration — it can hold days, seconds, and microseconds internally (though you can specify it in any combination of units).
Creating and Using timedelta
Create a timedelta by specifying the duration, then add or subtract it from a date or datetime object:
# timedelta_basics.py
from datetime import date, datetime, timedelta
today = date.today()
# Create timedeltas
one_week = timedelta(weeks=1)
two_days = timedelta(days=2)
ninety_days = timedelta(days=90)
print(f"Today: {today}")
print(f"One week from now: {today + one_week}")
print(f"Two days ago: {today - two_days}")
print(f"90 days from now: {today + ninety_days}")
# Timedelta from subtraction
deadline = date(2026, 12, 31)
days_remaining = deadline - today
print(f"\nDays until Dec 31: {days_remaining.days}")
# Timedelta with hours/minutes (use datetime)
start = datetime(2026, 4, 17, 9, 0, 0)
duration = timedelta(hours=2, minutes=30)
end = start + duration
print(f"\nMeeting start: {start.strftime('%H:%M')}")
print(f"Meeting end: {end.strftime('%H:%M')}")
Output:
Today: 2026-04-17
One week from now: 2026-04-24
Two days ago: 2026-04-15
90 days from now: 2026-07-16
Days until Dec 31: 258
Meeting start: 09:00
Meeting end: 11:30
When you subtract two dates, Python returns a timedelta object. Access its .days attribute for the integer count of days. For timedeltas involving hours, work with datetime objects instead of bare date objects — date has no concept of hours or minutes.
Formatting and Parsing Dates
Dates need to be displayed to users and parsed from user input, config files, API responses, and databases. Python provides strftime for formatting (datetime to string) and strptime for parsing (string to datetime).
Formatting with strftime
The strftime method formats a datetime using format codes. The most important codes to know:
| Code | Meaning | Example |
|---|---|---|
%Y | 4-digit year | 2026 |
%m | 2-digit month (01-12) | 04 |
%d | 2-digit day (01-31) | 17 |
%H | Hour (00-23, 24-hr) | 14 |
%I | Hour (01-12, 12-hr) | 02 |
%M | Minute (00-59) | 30 |
%S | Second (00-59) | 00 |
%p | AM or PM | PM |
%A | Full weekday name | Friday |
%B | Full month name | April |
%Z | Timezone name | UTC |
# strftime_examples.py
from datetime import datetime
now = datetime(2026, 4, 17, 14, 30, 0)
print(now.strftime("%Y-%m-%d")) # ISO format
print(now.strftime("%d/%m/%Y")) # UK format
print(now.strftime("%B %d, %Y")) # Human-readable
print(now.strftime("%A, %B %d, %Y")) # Full weekday
print(now.strftime("%I:%M %p")) # 12-hour clock
print(now.strftime("%Y-%m-%dT%H:%M:%S")) # ISO 8601 / API format
Output:
2026-04-17
17/04/2026
April 17, 2026
Friday, April 17, 2026
02:30 PM
2026-04-17T14:30:00
Parsing with strptime
When you receive a date as a string (from a form input, a CSV file, or an API response), use strptime to convert it into a datetime object. The format string must match the input exactly:
# strptime_examples.py
from datetime import datetime
# Parse common date formats
date_str1 = "2026-04-17"
dt1 = datetime.strptime(date_str1, "%Y-%m-%d")
print(f"Parsed ISO: {dt1}")
date_str2 = "April 17, 2026"
dt2 = datetime.strptime(date_str2, "%B %d, %Y")
print(f"Parsed long form: {dt2}")
date_str3 = "17/04/2026 14:30:00"
dt3 = datetime.strptime(date_str3, "%d/%m/%Y %H:%M:%S")
print(f"Parsed with time: {dt3}")
# Now you can do arithmetic on parsed dates
delta = dt1 - dt2 # Both represent 2026-04-17
print(f"Difference: {delta.days} days")
Output:
Parsed ISO: 2026-04-17 00:00:00
Parsed long form: 2026-04-17 00:00:00
Parsed with time: 2026-04-17 14:30:00
Difference: 0 days
A common mistake is mismatching the format string with the actual string. If the format doesn’t match, Python raises a ValueError. Always test your format string against real data before deploying to production.
Working with Timezones
By default, datetime objects created with datetime.now() are “naive” — they have no timezone information. This is fine for local scripts, but problematic for applications that serve global users or interact with APIs. Python’s timezone class provides simple fixed-offset timezone support:
# timezone_examples.py
from datetime import datetime, timezone, timedelta
# UTC-aware datetime
utc_now = datetime.now(timezone.utc)
print(f"UTC now: {utc_now}")
print(f"UTC offset: {utc_now.utcoffset()}")
# Create specific timezone offsets
eastern = timezone(timedelta(hours=-5)) # EST (UTC-5)
india = timezone(timedelta(hours=5, minutes=30)) # IST (UTC+5:30)
# Convert UTC to other timezones
eastern_time = utc_now.astimezone(eastern)
india_time = utc_now.astimezone(india)
print(f"\nEastern time: {eastern_time.strftime('%Y-%m-%d %H:%M %Z')}")
print(f"India time: {india_time.strftime('%Y-%m-%d %H:%M %Z')}")
# Compare aware datetimes
is_same = eastern_time == india_time
print(f"\nSame moment? {is_same}") # True -- same instant, different display
Output:
UTC now: 2026-04-17 07:23:45.123456+00:00
UTC offset: 0:00:00
Eastern time: 2026-04-17 02:23 UTC-05:00
India time: 2026-04-17 12:53 IST
Same moment? True
For production applications with complex timezone requirements (daylight saving time, historical timezone data), consider using the zoneinfo module (Python 3.9+) or the third-party pytz library, which include full IANA timezone database support.
Real-Life Example: Birthday Countdown Calculator
Let’s build a practical birthday calculator that tells you a person’s current age, how many days until their next birthday, and what day of the week it falls on:
# birthday_calculator.py
from datetime import date
def calculate_age(birthdate):
"""Calculate age in years from a birthdate."""
today = date.today()
age = today.year - birthdate.year
# Adjust if birthday hasn't occurred yet this year
if (today.month, today.day) < (birthdate.month, birthdate.day):
age -= 1
return age
def days_until_birthday(birthdate):
"""Return days until next birthday and the date it falls on."""
today = date.today()
# Next birthday this year
next_birthday = birthdate.replace(year=today.year)
# If birthday already passed this year, use next year
if next_birthday < today:
next_birthday = birthdate.replace(year=today.year + 1)
days_left = (next_birthday - today).days
return days_left, next_birthday
def birthday_report(name, birthdate_str):
"""Print a full birthday report for a person."""
birthdate = date.fromisoformat(birthdate_str) # Parses YYYY-MM-DD
age = calculate_age(birthdate)
days_left, next_bday = days_until_birthday(birthdate)
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday', 'Sunday']
bday_weekday = weekdays[next_bday.weekday()]
print(f"--- Birthday Report for {name} ---")
print(f"Birthdate: {birthdate.strftime('%B %d, %Y')}")
print(f"Age: {age} years old")
print(f"Next bday: {next_bday.strftime('%B %d, %Y')} ({bday_weekday})")
print(f"Countdown: {days_left} days to go")
if days_left == 0:
print(" ** Happy Birthday! **")
print()
birthday_report("Alice", "1990-07-15")
birthday_report("Bob", "1985-04-20")
birthday_report("Carol", "2000-12-31")
Output:
--- Birthday Report for Alice ---
Birthdate: July 15, 1990
Age: 35 years old
Next bday: July 15, 2026 (Wednesday)
Countdown: 89 days to go
--- Birthday Report for Bob ---
Birthdate: April 20, 1985
Age: 40 years old
Next bday: April 20, 2026 (Monday)
Countdown: 3 days to go
--- Birthday Report for Carol ---
Birthdate: December 31, 2000
Age: 25 years old
Next bday: December 31, 2026 (Thursday)
Countdown: 258 days to go
This project demonstrates several key concepts: using date.fromisoformat() to parse ISO-formatted date strings (a cleaner alternative to strptime for YYYY-MM-DD), using replace() to adjust a date's year while keeping month and day, subtracting dates to get day counts, and the subtle off-by-one logic needed to correctly calculate age. You could extend this by reading birthdays from a CSV file or sending email notifications when a birthday is approaching.
Frequently Asked Questions
How do I compare two dates in Python?
Use standard comparison operators (<, >, ==, <=, >=) directly on date or datetime objects. For example, if date1 < date2: works exactly as you'd expect. Just make sure both objects are the same type -- comparing a naive datetime with a timezone-aware one raises a TypeError.
How do I convert a Unix timestamp to a datetime?
Use datetime.fromtimestamp(ts) to convert a Unix timestamp (seconds since epoch) to a local datetime, or datetime.utcfromtimestamp(ts) for UTC. For a timezone-aware result, use datetime.fromtimestamp(ts, tz=timezone.utc). To go the other direction, call dt.timestamp() on any datetime object.
What is the easiest way to get an ISO 8601 formatted date string?
Call .isoformat() on any date or datetime object. This returns a standard ISO 8601 string like "2026-04-17" for dates or "2026-04-17T14:30:00" for datetimes. To parse ISO strings back into date objects, use date.fromisoformat() or datetime.fromisoformat() -- both were added in Python 3.7.
How do I find the first or last day of a month?
For the first day, use dt.replace(day=1). For the last day, use the calendar module: import calendar; last_day = calendar.monthrange(year, month)[1]. Then create the date with date(year, month, last_day). This handles the varying lengths of months (and leap years) correctly.
How do I measure elapsed time in seconds or milliseconds?
Subtract two datetime objects to get a timedelta, then call .total_seconds() on the result. For example: elapsed = (end_time - start_time).total_seconds(). For high-precision timing of code execution, use time.perf_counter() from the time module instead -- it's designed for benchmarking with sub-millisecond precision.
Conclusion
The datetime module gives you everything you need to work with dates and times in Python without installing third-party libraries. In this tutorial, you learned how to create date and datetime objects with date.today(), datetime.now(), and constructor calls; do date arithmetic using timedelta; format datetimes into strings with strftime and its format codes; parse date strings back to datetime objects with strptime and fromisoformat; and handle timezones with the built-in timezone class.
The birthday calculator project shows how these pieces fit together in a real application. Try extending it: read birthdays from a CSV file using the csv module, sort the list by upcoming birthday, or send a Telegram notification when a birthday is fewer than 7 days away.
For full documentation and additional classes, see the official Python datetime documentation. For complex timezone requirements, explore the zoneinfo module added in Python 3.9.