Foundation Guide

Python Standard Library: datetime

šŸ“„ 36 Pages šŸŽÆ Dates, times, durations, timezones

What this page does

Introduces the datetime module and what you will learn.

Where this fits

This is the starting point. You should know basic Python before starting.

Explanation

The datetime module provides classes for manipulating dates and times. By the end of this guide, you will know how to:

  • Create dates — Specific days, today's date
  • Create times — Hours, minutes, seconds, microseconds
  • Create datetimes — Combined date and time
  • Calculate durations — Add/subtract time with timedelta
  • Format output — Convert to any string format
  • Parse input — Convert strings to datetime objects
  • Handle timezones — UTC and local time
The datetime module is built into Python. No installation required.

Why this matters

Every application deals with dates: birthdays, deadlines, schedules, logs, expiration dates, billing cycles. datetime is essential.

āœ“ Checkpoint

⚠ If something breaks here

Nothing to break yet. Move to Page 2.

What this page does

Sets up a Python file for practicing the datetime module.

Where this fits

Before writing code, create a workspace.

Code (this page)

mkdir ~/projects/datetime-practice
cd ~/projects/datetime-practice
touch datetime_basics.py

Explanation

These commands:

  • Create a folder called datetime-practice
  • Enter that folder
  • Create an empty Python file
From now on, you will write code in datetime_basics.py and run it with python3 datetime_basics.py.

Why this matters

Having a dedicated file lets you build up code incrementally and re-run to test changes.

āœ“ Checkpoint

⚠ If something breaks here

  • mkdir: File exists: The folder exists. Just cd into it

What this page does

Shows the different ways to import from the datetime module.

Where this fits

Your file is ready. Now understand the import options.

Code (this page)

# Option 1: Import the module
import datetime
print(datetime.date.today())

# Option 2: Import specific classes (most common) from datetime import date, time, datetime, timedelta print(date.today())

# Note: 'datetime' is both a module AND a class inside it # This can be confusing at first

Explanation

The datetime module contains several classes:

  • date — just a date (year, month, day)
  • time — just a time (hour, minute, second)
  • datetime — date AND time combined
  • timedelta — a duration (difference between times)
Most code uses from datetime import ... to avoid writing datetime.datetime.

Why this matters

Understanding the import clarifies the confusing datetime.datetime pattern you'll see in code.

āœ“ Checkpoint

⚠ If something breaks here

  • NameError: Make sure you imported the class you're using

What this page does

Creates a date object for a specific day.

Where this fits

Module imported. Now create your first date.

Code (this page)

from datetime import date

# Create a specific date birthday = date(2000, 6, 15) # Year, Month, Day print("Birthday:", birthday)

# Access components print(f"Year: {birthday.year}") print(f"Month: {birthday.month}") print(f"Day: {birthday.day}")

# Invalid dates raise errors # date(2026, 2, 30) # ValueError: day is out of range for month

Explanation

Run the file:

Birthday: 2000-06-15
Year: 2000
Month: 6
Day: 15

date(year, month, day):

  • Year: any valid year (1-9999)
  • Month: 1-12
  • Day: 1-31 (validated against the month)
Invalid dates (Feb 30, Month 13) raise ValueError.

Why this matters

Dates are the foundation. Birthdays, deadlines, events — all start with a date object.

āœ“ Checkpoint

⚠ If something breaks here

  • ValueError: Check that month is 1-12 and day is valid for that month

What this page does

Gets the current date.

Where this fits

You created specific dates. Now get today.

Code (this page)

from datetime import date

today = date.today() print("Today is:", today) print(f"Year: {today.year}") print(f"Month: {today.month}") print(f"Day: {today.day}")

# Weekday (0=Monday, 6=Sunday) print(f"Weekday number: {today.weekday()}")

# ISO weekday (1=Monday, 7=Sunday) print(f"ISO weekday: {today.isoweekday()}")

Explanation

Run the file:

Today is: 2026-01-28
Year: 2026
Month: 1
Day: 28
Weekday number: 2
ISO weekday: 3

date.today():

  • Returns current local date
  • .weekday() returns 0-6 (Monday=0)
  • .isoweekday() returns 1-7 (Monday=1)

Why this matters

"What's today?" is a common question. Most date logic starts from today.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. Date is always available.

What this page does

Creates a time object for a specific time of day.

Where this fits

You know dates. Now learn times (without dates).

Code (this page)

from datetime import time

# Create a specific time meeting = time(14, 30) # 2:30 PM print("Meeting time:", meeting)

# With seconds and microseconds precise = time(14, 30, 45, 123456) print("Precise time:", precise)

# Access components print(f"Hour: {meeting.hour}") print(f"Minute: {meeting.minute}") print(f"Second: {meeting.second}") print(f"Microsecond: {meeting.microsecond}")

Explanation

Run the file:

Meeting time: 14:30:00
Precise time: 14:30:45.123456
Hour: 14
Minute: 30
Second: 0
Microsecond: 0

time(hour, minute, second, microsecond):

  • Hour: 0-23 (24-hour format)
  • Minute: 0-59
  • Second: 0-59
  • Microsecond: 0-999999
All parameters except hour are optional (default to 0).

Why this matters

Some applications need time without date: schedules, alarms, business hours.

āœ“ Checkpoint

⚠ If something breaks here

  • ValueError: Hour must be 0-23, minute/second 0-59

What this page does

Creates a datetime object combining date and time.

Where this fits

You know date and time separately. Now combine them.

Code (this page)

from datetime import datetime

# Create a specific datetime event = datetime(2026, 12, 25, 18, 30, 0) print("Event:", event)

# Access date components print(f"Year: {event.year}") print(f"Month: {event.month}") print(f"Day: {event.day}")

# Access time components print(f"Hour: {event.hour}") print(f"Minute: {event.minute}") print(f"Second: {event.second}")

Explanation

Run the file:

Event: 2026-12-25 18:30:00
Year: 2026
Month: 12
Day: 25
Hour: 18
Minute: 30
Second: 0

datetime(year, month, day, hour, minute, second, microsecond):

  • First three (date) are required
  • Last four (time) default to 0

Why this matters

Most real events have both date AND time: appointments, log entries, transactions.

āœ“ Checkpoint

⚠ If something breaks here

  • TypeError: Year, month, day are required

What this page does

Gets the current date and time.

Where this fits

You created specific datetimes. Now get the current moment.

Code (this page)

from datetime import datetime

# Current local datetime now = datetime.now() print("Now:", now)

# Current UTC datetime utc_now = datetime.utcnow() print("UTC:", utc_now)

# Just the date part print("Date part:", now.date())

# Just the time part print("Time part:", now.time())

Explanation

Run the file:

Now: 2026-01-28 14:30:45.123456
UTC: 2026-01-28 22:30:45.123456
Date part: 2026-01-28
Time part: 14:30:45.123456

datetime.now():

  • Returns current local datetime
  • .date() extracts just the date
  • .time() extracts just the time
datetime.utcnow():
  • Returns current UTC datetime (no timezone info attached)

Why this matters

Logging, timestamps, "created at" fields — all need the current datetime.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. Now always works.

What this page does

Creates duration objects for time arithmetic.

Where this fits

You have datetimes. Now calculate differences.

Code (this page)

from datetime import timedelta

# Create durations one_day = timedelta(days=1) one_week = timedelta(weeks=1) two_hours = timedelta(hours=2) thirty_minutes = timedelta(minutes=30)

print("One day:", one_day) print("One week:", one_week) print("Two hours:", two_hours) print("30 minutes:", thirty_minutes)

# Combined complex_duration = timedelta(days=2, hours=5, minutes=30) print("2 days, 5 hours, 30 min:", complex_duration)

Explanation

Run the file:

One day: 1 day, 0:00:00
One week: 7 days, 0:00:00
Two hours: 2:00:00
30 minutes: 0:30:00
2 days, 5 hours, 30 min: 2 days, 5:30:00

timedelta(weeks, days, hours, minutes, seconds, milliseconds, microseconds):

  • All parameters are optional
  • Internally stored as days, seconds, and microseconds
  • Weeks are converted to days (1 week = 7 days)

Why this matters

Timedelta is how you do date math: "What's 30 days from now?" "How long until deadline?"

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. All parameters are optional.

What this page does

Adds and subtracts time from dates.

Where this fits

You have dates and timedeltas. Now combine them.

Code (this page)

from datetime import date, timedelta

today = date.today() print("Today:", today)

# Add days tomorrow = today + timedelta(days=1) next_week = today + timedelta(weeks=1) print("Tomorrow:", tomorrow) print("Next week:", next_week)

# Subtract days yesterday = today - timedelta(days=1) last_month = today - timedelta(days=30) print("Yesterday:", yesterday) print("30 days ago:", last_month)

# Difference between dates new_years = date(2027, 1, 1) days_until = new_years - today print(f"Days until 2027: {days_until.days}")

Explanation

Run the file:

Today: 2026-01-28
Tomorrow: 2026-01-29
Next week: 2026-02-04
Yesterday: 2026-01-27
30 days ago: 2025-12-29
Days until 2027: 338

Arithmetic rules:

  • date + timedelta = date
  • date - timedelta = date
  • date - date = timedelta

Why this matters

"Due in 30 days", "Expires in 1 year", "Days since signup" — all date arithmetic.

āœ“ Checkpoint

⚠ If something breaks here

  • TypeError: Can only add/subtract timedelta to dates, not integers

What this page does

Adds and subtracts time from datetimes (including hours/minutes).

Where this fits

Date arithmetic works. Now add time precision.

Code (this page)

from datetime import datetime, timedelta

now = datetime.now() print("Now:", now)

# Add hours and minutes meeting_start = now + timedelta(hours=2) meeting_end = meeting_start + timedelta(minutes=45) print("Meeting starts:", meeting_start) print("Meeting ends:", meeting_end)

# Subtract to find past times three_hours_ago = now - timedelta(hours=3) print("3 hours ago:", three_hours_ago)

# Difference between datetimes deadline = datetime(2026, 2, 1, 9, 0, 0) remaining = deadline - now print(f"Time until deadline: {remaining}") print(f"That's {remaining.days} days and {remaining.seconds // 3600} hours")

Explanation

Run the file:

Now: 2026-01-28 14:30:00.123456
Meeting starts: 2026-01-28 16:30:00.123456
Meeting ends: 2026-01-28 17:15:00.123456
3 hours ago: 2026-01-28 11:30:00.123456
Time until deadline: 3 days, 18:30:00
That's 3 days and 18 hours

Same rules as date:

  • datetime + timedelta = datetime
  • datetime - timedelta = datetime
  • datetime - datetime = timedelta

Why this matters

Scheduling, countdowns, session expiration — all need datetime precision.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. Same patterns as date arithmetic.

What this page does

Shows how to compare dates and datetimes.

Where this fits

You can calculate differences. Now compare directly.

Code (this page)

from datetime import date, datetime

# Compare dates today = date.today() birthday = date(2026, 6, 15)

print(f"Today: {today}") print(f"Birthday: {birthday}") print(f"Birthday passed? {today > birthday}") print(f"Birthday upcoming? {today < birthday}") print(f"Is today birthday? {today == birthday}")

# Compare datetimes now = datetime.now() deadline = datetime(2026, 2, 1, 17, 0, 0)

if now < deadline: print("\nDeadline is in the future") elif now > deadline: print("\nDeadline has passed") else: print("\nIt's exactly the deadline!")

Explanation

Run the file:

Today: 2026-01-28
Birthday: 2026-06-15
Birthday passed? False
Birthday upcoming? True
Is today birthday? False

Deadline is in the future

Comparison operators work naturally:

  • < earlier than
  • > later than
  • == same moment
  • <=, >=, != also work

Why this matters

"Is this expired?", "Has the event started?", "Is the user's subscription active?" — all comparisons.

āœ“ Checkpoint

⚠ If something breaks here

  • TypeError: Can only compare date with date, datetime with datetime

What this page does

Converts datetime to formatted strings.

Where this fits

You have datetime objects. Now display them nicely.

Code (this page)

from datetime import datetime

now = datetime.now()

# Common formats print(now.strftime("%Y-%m-%d")) # 2026-01-28 print(now.strftime("%d/%m/%Y")) # 28/01/2026 print(now.strftime("%B %d, %Y")) # January 28, 2026 print(now.strftime("%H:%M:%S")) # 14:30:45 print(now.strftime("%I:%M %p")) # 02:30 PM print(now.strftime("%A, %B %d, %Y")) # Wednesday, January 28, 2026 print(now.strftime("%Y-%m-%d %H:%M:%S")) # 2026-01-28 14:30:45

# Custom formats print(now.strftime("Today is %A")) # Today is Wednesday print(now.strftime("Time: %H:%M")) # Time: 14:30

Explanation

Run the file:

2026-01-28
28/01/2026
January 28, 2026
14:30:45
02:30 PM
Wednesday, January 28, 2026
2026-01-28 14:30:45
Today is Wednesday
Time: 14:30

strftime(format) replaces codes with values:

  • %Y = 4-digit year
  • %m = month (01-12)
  • %d = day (01-31)
  • %H = hour 24h, %I = hour 12h
  • %M = minute, %S = second
  • %A = weekday name, %B = month name

Why this matters

Users don't read 2026-01-28 14:30:00.123456. They read "January 28, 2026 at 2:30 PM".

āœ“ Checkpoint

⚠ If something breaks here

  • Wrong output: Format codes are case-sensitive (%m ≠ %M)

What this page does

Converts strings to datetime objects.

Where this fits

strftime outputs strings. strptime parses them back.

Code (this page)

from datetime import datetime

# Parse different formats dt1 = datetime.strptime("2026-01-28", "%Y-%m-%d") print("Parsed:", dt1)

dt2 = datetime.strptime("28/01/2026", "%d/%m/%Y") print("Parsed:", dt2)

dt3 = datetime.strptime("January 28, 2026", "%B %d, %Y") print("Parsed:", dt3)

dt4 = datetime.strptime("2026-01-28 14:30:00", "%Y-%m-%d %H:%M:%S") print("Parsed with time:", dt4)

# Parse from user input date_string = "2026-12-25" christmas = datetime.strptime(date_string, "%Y-%m-%d") print(f"Christmas is on a {christmas.strftime('%A')}")

Explanation

Run the file:

Parsed: 2026-01-28 00:00:00
Parsed: 2026-01-28 00:00:00
Parsed: 2026-01-28 00:00:00
Parsed with time: 2026-01-28 14:30:00
Christmas is on a Friday

datetime.strptime(string, format):

  • Parses string according to format
  • Returns datetime object
  • Time defaults to 00:00:00 if not in format

Why this matters

User input, CSV files, API responses — all give you date strings that need parsing.

āœ“ Checkpoint

⚠ If something breaks here

  • ValueError: Format doesn't match the string. Check carefully.

What this page does

Shows the standard ISO 8601 date format.

Where this fits

Custom formats vary. ISO is the universal standard.

Code (this page)

from datetime import datetime, date

# ISO format output now = datetime.now() print("ISO datetime:", now.isoformat())

today = date.today() print("ISO date:", today.isoformat())

# With custom separator print("Custom sep:", now.isoformat(sep=' '))

# Parse ISO format iso_string = "2026-01-28T14:30:00" parsed = datetime.fromisoformat(iso_string) print("Parsed ISO:", parsed)

# Also works with date date_string = "2026-01-28" parsed_date = date.fromisoformat(date_string) print("Parsed date:", parsed_date)

Explanation

Run the file:

ISO datetime: 2026-01-28T14:30:45.123456
ISO date: 2026-01-28
Custom sep: 2026-01-28 14:30:45.123456
Parsed ISO: 2026-01-28 14:30:00
Parsed date: 2026-01-28

ISO 8601 format: YYYY-MM-DDTHH:MM:SS

  • Universal, unambiguous
  • Used by APIs, databases, JSON
  • .isoformat() outputs it
  • .fromisoformat() parses it (Python 3.7+)

Why this matters

APIs and databases expect ISO format. It sorts correctly as text. It's the safest choice.

āœ“ Checkpoint

⚠ If something breaks here

  • fromisoformat not found: Requires Python 3.7+

What this page does

Converts between datetime and Unix timestamps.

Where this fits

Sometimes you need timestamps instead of datetime objects.

Code (this page)

from datetime import datetime

# Datetime to timestamp now = datetime.now() timestamp = now.timestamp() print(f"Datetime: {now}") print(f"Timestamp: {timestamp}")

# Timestamp to datetime ts = 1737849600 # A specific moment dt = datetime.fromtimestamp(ts) print(f"\nTimestamp {ts}") print(f"As datetime: {dt}")

# UTC timestamp to datetime dt_utc = datetime.utcfromtimestamp(ts) print(f"As UTC: {dt_utc}")

# Current timestamp shortcut import time print(f"\ntime.time(): {time.time()}") print(f"datetime.now().timestamp(): {datetime.now().timestamp()}")

Explanation

Run the file:

Datetime: 2026-01-28 14:30:45.123456
Timestamp: 1737849045.123456

Timestamp 1737849600 As datetime: 2026-01-28 14:40:00 As UTC: 2026-01-28 22:40:00

time.time(): 1737849045.123456 datetime.now().timestamp(): 1737849045.123456

Conversion methods:

  • .timestamp() → datetime to Unix timestamp
  • fromtimestamp(ts) → timestamp to local datetime
  • utcfromtimestamp(ts) → timestamp to UTC datetime

Why this matters

Databases often store timestamps. APIs return them. You need to convert both ways.

āœ“ Checkpoint

⚠ If something breaks here

  • OSError: Timestamp out of valid range

What this page does

Creates new datetime by changing specific parts.

Where this fits

Sometimes you need to modify just one component.

Code (this page)

from datetime import datetime

now = datetime.now() print("Now:", now)

# Change the year next_year = now.replace(year=2027) print("Next year:", next_year)

# Change multiple parts midnight = now.replace(hour=0, minute=0, second=0, microsecond=0) print("Today at midnight:", midnight)

# Set specific time noon = now.replace(hour=12, minute=0, second=0, microsecond=0) print("Today at noon:", noon)

# End of month end_of_month = now.replace(day=31) # Might fail if month doesn't have 31 days # Safer: use calendar or go to next month and subtract

Explanation

Run the file:

Now: 2026-01-28 14:30:45.123456
Next year: 2027-01-28 14:30:45.123456
Today at midnight: 2026-01-28 00:00:00
Today at noon: 2026-01-28 12:00:00

dt.replace(year=, month=, day=, hour=, minute=, second=, microsecond=):

  • Returns a NEW datetime (original unchanged)
  • Only specify the parts you want to change
  • Invalid combinations raise ValueError

Why this matters

"Same time tomorrow", "start of today", "change to midnight" — all need replace().

āœ“ Checkpoint

⚠ If something breaks here

  • ValueError: day=31 doesn't work for all months

What this page does

Shows how to work with days of the week.

Where this fits

Many schedules depend on weekdays.

Code (this page)

from datetime import date, timedelta

today = date.today() print(f"Today: {today} ({today.strftime('%A')})") print(f"Weekday: {today.weekday()} (0=Mon, 6=Sun)")

# Find next Monday days_until_monday = (7 - today.weekday()) % 7 if days_until_monday == 0: days_until_monday = 7 # If today is Monday, get next Monday next_monday = today + timedelta(days=days_until_monday) print(f"Next Monday: {next_monday}")

# Find last Friday days_since_friday = (today.weekday() - 4) % 7 if days_since_friday == 0: days_since_friday = 7 # If today is Friday, get last Friday last_friday = today - timedelta(days=days_since_friday) print(f"Last Friday: {last_friday}")

# Is it a weekend? is_weekend = today.weekday() >= 5 print(f"Is weekend: {is_weekend}")

# Days until weekend if today.weekday() < 5: # Weekday days_to_weekend = 5 - today.weekday() print(f"Days until weekend: {days_to_weekend}") else: print("It's the weekend!")

Explanation

Run the file (results depend on current day):

Today: 2026-01-28 (Wednesday)
Weekday: 2 (0=Mon, 6=Sun)
Next Monday: 2026-02-02
Last Friday: 2026-01-23
Is weekend: False
Days until weekend: 2

Weekday math:

  • Monday=0, Sunday=6
  • Use modulo % 7 for wrapping
  • Weekend = weekday >= 5

Why this matters

Business logic often depends on weekdays: "next business day", "last Friday", "skip weekends".

āœ“ Checkpoint

⚠ If something breaks here

  • Off by one: Double-check your modulo math

What this page does

Introduces timezone-aware datetimes.

Where this fits

So far, all datetimes are "naive" (no timezone). Now add awareness.

Code (this page)

from datetime import datetime, timezone

# Naive datetime (no timezone info) naive = datetime.now() print(f"Naive: {naive}") print(f"Timezone info: {naive.tzinfo}")

# UTC timezone-aware datetime utc_now = datetime.now(timezone.utc) print(f"\nUTC aware: {utc_now}") print(f"Timezone info: {utc_now.tzinfo}")

# Check if aware or naive print(f"\nNaive has tz? {naive.tzinfo is not None}") print(f"UTC has tz? {utc_now.tzinfo is not None}")

Explanation

Run the file:

Naive: 2026-01-28 14:30:45.123456
Timezone info: None

UTC aware: 2026-01-28 22:30:45.123456+00:00 Timezone info: UTC

Naive has tz? False UTC has tz? True

Two types of datetime:

  • Naive: No timezone info (.tzinfo is None)
  • Aware: Has timezone info
timezone.utc is the built-in UTC timezone.

Why this matters

Global applications need timezone awareness. "3 PM" means different things in different places.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. Timezone is built-in.

What this page does

Creates custom timezone offsets.

Where this fits

UTC is one timezone. Now handle others.

Code (this page)

from datetime import datetime, timezone, timedelta

# Create timezone with offset eastern = timezone(timedelta(hours=-5)) # EST (UTC-5) pacific = timezone(timedelta(hours=-8)) # PST (UTC-8) india = timezone(timedelta(hours=5, minutes=30)) # IST (UTC+5:30)

# Get current time in different zones utc_now = datetime.now(timezone.utc) print(f"UTC: {utc_now.strftime('%H:%M %Z')}")

eastern_now = utc_now.astimezone(eastern) print(f"Eastern: {eastern_now.strftime('%H:%M %Z')}")

pacific_now = utc_now.astimezone(pacific) print(f"Pacific: {pacific_now.strftime('%H:%M %Z')}")

india_now = utc_now.astimezone(india) print(f"India: {india_now.strftime('%H:%M %Z')}")

Explanation

Run the file:

UTC:     22:30 UTC
Eastern: 17:30 UTC-05:00
Pacific: 14:30 UTC-08:00
India:   04:00 UTC+05:30

timezone(timedelta(...)):

  • Creates a fixed offset timezone
  • astimezone(tz) converts between timezones
  • Same moment in time, different local representation

Why this matters

Scheduling across timezones needs conversion. "Meeting at 3 PM Eastern" → "What time is that for me?"

āœ“ Checkpoint

⚠ If something breaks here

  • Wrong offset: Remember negative for west of UTC

What this page does

Shows the modern way to handle named timezones.

Where this fits

Fixed offsets don't handle daylight saving. Named zones do.

Code (this page)

from datetime import datetime
try:
    from zoneinfo import ZoneInfo
except ImportError:
    print("zoneinfo requires Python 3.9+")
    exit()

# Named timezones (handle DST automatically) eastern = ZoneInfo("America/New_York") pacific = ZoneInfo("America/Los_Angeles") london = ZoneInfo("Europe/London") tokyo = ZoneInfo("Asia/Tokyo")

# Current time in each zone now_utc = datetime.now(ZoneInfo("UTC")) print(f"UTC: {now_utc.strftime('%Y-%m-%d %H:%M %Z')}")

now_eastern = now_utc.astimezone(eastern) print(f"New York: {now_eastern.strftime('%Y-%m-%d %H:%M %Z')}")

now_pacific = now_utc.astimezone(pacific) print(f"LA: {now_pacific.strftime('%Y-%m-%d %H:%M %Z')}")

now_london = now_utc.astimezone(london) print(f"London: {now_london.strftime('%Y-%m-%d %H:%M %Z')}")

now_tokyo = now_utc.astimezone(tokyo) print(f"Tokyo: {now_tokyo.strftime('%Y-%m-%d %H:%M %Z')}")

Explanation

Run the file:

UTC:      2026-01-28 22:30 UTC
New York: 2026-01-28 17:30 EST
LA:       2026-01-28 14:30 PST
London:   2026-01-28 22:30 GMT
Tokyo:    2026-01-29 07:30 JST

ZoneInfo("timezone_name"):

  • Uses IANA timezone database
  • Handles daylight saving automatically
  • Names like "America/New_York", "Europe/London"

Why this matters

Daylight saving changes offsets. Named zones handle this correctly.

āœ“ Checkpoint

⚠ If something breaks here

  • ModuleNotFoundError: Requires Python 3.9+
  • ZoneInfoNotFoundError: Invalid timezone name

What this page does

Adds timezone info to naive datetimes.

Where this fits

Legacy code often has naive datetimes. Convert them.

Code (this page)

from datetime import datetime, timezone, timedelta

# Naive datetime naive = datetime(2026, 6, 15, 14, 30) print(f"Naive: {naive} (tzinfo={naive.tzinfo})")

# Add UTC timezone (assume it WAS UTC) utc_aware = naive.replace(tzinfo=timezone.utc) print(f"As UTC: {utc_aware}")

# Add custom timezone eastern = timezone(timedelta(hours=-5)) eastern_aware = naive.replace(tzinfo=eastern) print(f"As Eastern: {eastern_aware}")

# IMPORTANT: replace doesn't convert, it labels! # These represent DIFFERENT moments in time: print(f"\nUTC timestamp: {utc_aware.timestamp()}") print(f"Eastern timestamp: {eastern_aware.timestamp()}") print("(Different because they're different actual times)")

Explanation

Run the file:

Naive: 2026-06-15 14:30:00 (tzinfo=None)
As UTC: 2026-06-15 14:30:00+00:00
As Eastern: 2026-06-15 14:30:00-05:00

UTC timestamp: 1750000200.0 Eastern timestamp: 1750018200.0 (Different because they're different actual times)

replace(tzinfo=tz):

  • LABELS the datetime with a timezone
  • Does NOT convert the time
  • Use when you know what timezone the naive datetime was in

Why this matters

Converting naive to aware is common when reading from databases or files that don't store timezone.

āœ“ Checkpoint

⚠ If something breaks here

  • Can't mix aware and naive in comparisons

What this page does

Shows frequently needed date calculations.

Where this fits

Build practical skills with real scenarios.

Code (this page)

from datetime import date, datetime, timedelta

today = date.today()

# First day of current month first_of_month = today.replace(day=1) print(f"First of month: {first_of_month}")

# Last day of current month next_month = (today.replace(day=28) + timedelta(days=4)).replace(day=1) last_of_month = next_month - timedelta(days=1) print(f"Last of month: {last_of_month}")

# First day of current year first_of_year = today.replace(month=1, day=1) print(f"First of year: {first_of_year}")

# Days remaining in year dec_31 = date(today.year, 12, 31) days_remaining = (dec_31 - today).days print(f"Days left in year: {days_remaining}")

# Age calculation birthdate = date(1990, 6, 15) age = today.year - birthdate.year if (today.month, today.day) < (birthdate.month, birthdate.day): age -= 1 # Birthday hasn't happened yet this year print(f"Age: {age} years")

Explanation

Run the file:

First of month: 2026-01-01
Last of month: 2026-01-31
First of year: 2026-01-01
Days left in year: 337
Age: 35 years

Common patterns:

  • First of month: replace(day=1)
  • Last of month: go to next month, subtract 1 day
  • Age: subtract years, adjust if birthday hasn't passed

Why this matters

These calculations appear constantly in business applications.

āœ“ Checkpoint

⚠ If something breaks here

  • Last of month logic: handles months with 28-31 days correctly

What this page does

Creates sequences of dates.

Where this fits

Reports and calendars need date ranges.

Code (this page)

from datetime import date, timedelta

def date_range(start, end): """Generate dates from start to end (inclusive).""" current = start while current <= end: yield current current += timedelta(days=1)

# Generate a week of dates start = date(2026, 1, 26) end = date(2026, 2, 1)

print("Week of dates:") for d in date_range(start, end): print(f" {d} ({d.strftime('%A')})")

# Generate working days only print("\nWorking days only:") for d in date_range(start, end): if d.weekday() < 5: # Monday-Friday print(f" {d}")

# Count days total = sum(1 for d in date_range(start, end)) work_days = sum(1 for d in date_range(start, end) if d.weekday() < 5) print(f"\nTotal: {total} days, {work_days} working days")

Explanation

Run the file:

Week of dates:
  2026-01-26 (Monday)
  2026-01-27 (Tuesday)
  2026-01-28 (Wednesday)
  2026-01-29 (Thursday)
  2026-01-30 (Friday)
  2026-01-31 (Saturday)
  2026-02-01 (Sunday)

Working days only: 2026-01-26 2026-01-27 2026-01-28 2026-01-29 2026-01-30

Total: 7 days, 5 working days

Generator pattern:

  • Use yield for memory efficiency
  • Filter with conditions (weekday < 5)
  • Works with any date range

Why this matters

Calendar views, billing periods, scheduling — all need date ranges.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. Adjust start/end as needed.

What this page does

Handles dates in various formats from different sources.

Where this fits

Real data comes in many formats. Parse them all.

Code (this page)

from datetime import datetime

def parse_date_flexible(date_string): """Try multiple formats to parse a date string.""" formats = [ "%Y-%m-%d", # 2026-01-28 "%d/%m/%Y", # 28/01/2026 "%m/%d/%Y", # 01/28/2026 "%B %d, %Y", # January 28, 2026 "%d %B %Y", # 28 January 2026 "%Y-%m-%d %H:%M:%S", # 2026-01-28 14:30:00 "%d/%m/%Y %H:%M", # 28/01/2026 14:30 ] for fmt in formats: try: return datetime.strptime(date_string, fmt) except ValueError: continue raise ValueError(f"Cannot parse date: {date_string}")

# Test various formats test_dates = [ "2026-01-28", "28/01/2026", "January 28, 2026", "2026-01-28 14:30:00", ]

for date_str in test_dates: parsed = parse_date_flexible(date_str) print(f"'{date_str}' → {parsed}")

Explanation

Run the file:

'2026-01-28' → 2026-01-28 00:00:00
'28/01/2026' → 2026-01-28 00:00:00
'January 28, 2026' → 2026-01-28 00:00:00
'2026-01-28 14:30:00' → 2026-01-28 14:30:00

The pattern:

  • Try each format in order
  • Return on first success
  • Raise error if none match

Why this matters

Users enter dates differently. Files from different systems use different formats. Be flexible.

āœ“ Checkpoint

⚠ If something breaks here

  • Ambiguous dates: "01/02/2026" could be Jan 2 or Feb 1. Order your formats by priority.

What this page does

Converts timedelta to human-readable strings.

Where this fits

Raw timedelta output isn't user-friendly.

Code (this page)

from datetime import timedelta

def format_duration(td): """Convert timedelta to human-readable string.""" total_seconds = int(td.total_seconds()) if total_seconds < 0: return "in the past" days, remainder = divmod(total_seconds, 86400) hours, remainder = divmod(remainder, 3600) minutes, seconds = divmod(remainder, 60) parts = [] if days: parts.append(f"{days} day{'s' if days != 1 else ''}") if hours: parts.append(f"{hours} hour{'s' if hours != 1 else ''}") if minutes: parts.append(f"{minutes} minute{'s' if minutes != 1 else ''}") if seconds and not days: # Skip seconds for long durations parts.append(f"{seconds} second{'s' if seconds != 1 else ''}") return ", ".join(parts) if parts else "0 seconds"

# Test various durations durations = [ timedelta(seconds=45), timedelta(minutes=5, seconds=30), timedelta(hours=2, minutes=15), timedelta(days=1, hours=5), timedelta(days=30), ]

for td in durations: print(f"{td} → {format_duration(td)}")

Explanation

Run the file:

0:00:45 → 45 seconds
0:05:30 → 5 minutes, 30 seconds
2:15:00 → 2 hours, 15 minutes
1 day, 5:00:00 → 1 day, 5 hours
30 days, 0:00:00 → 30 days

Key techniques:

  • total_seconds() gives total as float
  • divmod extracts each unit
  • Conditional plural handling

Why this matters

"3 days, 2 hours" is readable. "3 days, 2:00:00" is not.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. Adjust formatting to your needs.

What this page does

Builds a countdown to a future event.

Where this fits

Apply datetime skills to a practical tool.

Code (this page)

from datetime import datetime, timedelta

def countdown_to(event_name, event_datetime): """Print countdown to an event.""" now = datetime.now() remaining = event_datetime - now if remaining.total_seconds() <= 0: print(f"{event_name} has already happened!") return days = remaining.days hours, remainder = divmod(remaining.seconds, 3600) minutes, seconds = divmod(remainder, 60) print(f"Countdown to {event_name}:") print(f" {days} days, {hours} hours, {minutes} minutes, {seconds} seconds") # Milestones if days == 0: print(" šŸ”„ It's TODAY!") elif days <= 7: print(" ⚔ Less than a week!") elif days <= 30: print(" šŸ“… Less than a month!")

# Countdown to various events new_year = datetime(2027, 1, 1, 0, 0, 0) countdown_to("New Year 2027", new_year)

print()

# Countdown to a meeting meeting = datetime.now() + timedelta(hours=3, minutes=30) countdown_to("Team Meeting", meeting)

Explanation

Run the file:

Countdown to New Year 2027:
  337 days, 9 hours, 29 minutes, 15 seconds
  šŸ“… Less than a month!

Countdown to Team Meeting: 0 days, 3 hours, 29 minutes, 59 seconds šŸ”„ It's TODAY!

Pattern:

  • Calculate remaining time as timedelta
  • Extract components for display
  • Add contextual messages based on duration

Why this matters

Countdowns create anticipation and urgency. Events, deadlines, launches.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. Event in past shows message.

What this page does

Manages events with datetime comparisons.

Where this fits

Real applications manage multiple events.

Code (this page)

from datetime import datetime, timedelta

class Event: def __init__(self, name, dt): self.name = name self.datetime = dt def __repr__(self): return f"{self.name} @ {self.datetime.strftime('%Y-%m-%d %H:%M')}"

class Scheduler: def __init__(self): self.events = [] def add(self, name, dt): self.events.append(Event(name, dt)) self.events.sort(key=lambda e: e.datetime) def upcoming(self, n=5): """Get next n upcoming events.""" now = datetime.now() future = [e for e in self.events if e.datetime > now] return future[:n] def today(self): """Get today's events.""" now = datetime.now() return [e for e in self.events if e.datetime.date() == now.date()]

# Demo scheduler = Scheduler() now = datetime.now()

scheduler.add("Team standup", now + timedelta(hours=1)) scheduler.add("Lunch", now + timedelta(hours=3)) scheduler.add("Code review", now + timedelta(hours=5)) scheduler.add("Tomorrow meeting", now + timedelta(days=1, hours=2)) scheduler.add("Past event", now - timedelta(hours=2))

print("All events:", scheduler.events) print("\nUpcoming:", scheduler.upcoming(3)) print("\nToday:", scheduler.today())

Explanation

Run the file:

All events: [Past event @ ..., Team standup @ ..., Lunch @ ..., ...]

Upcoming: [Team standup @ ..., Lunch @ ..., Code review @ ...]

Today: [Team standup @ ..., Lunch @ ..., Code review @ ...]

Scheduler features:

  • Events sorted by datetime
  • Filter by upcoming/past
  • Filter by date

Why this matters

Calendar apps, reminder systems, booking systems — all schedule events.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. Adjust event times as needed.

What this page does

Generates repeating events (daily, weekly, monthly).

Where this fits

Real schedules have recurring events.

Code (this page)

from datetime import datetime, timedelta, date

def daily_occurrences(start_date, count): """Generate daily occurrences.""" for i in range(count): yield start_date + timedelta(days=i)

def weekly_occurrences(start_date, count, weekday=None): """Generate weekly occurrences on same weekday.""" if weekday is None: weekday = start_date.weekday() # Find first occurrence on target weekday days_ahead = weekday - start_date.weekday() if days_ahead < 0: days_ahead += 7 first = start_date + timedelta(days=days_ahead) for i in range(count): yield first + timedelta(weeks=i)

def monthly_occurrences(start_date, count): """Generate monthly occurrences (same day of month).""" current = start_date for _ in range(count): yield current # Move to next month if current.month == 12: current = current.replace(year=current.year + 1, month=1) else: # Handle months with fewer days next_month = current.month + 1 try: current = current.replace(month=next_month) except ValueError: # Day doesn't exist in next month, use last day current = current.replace(month=next_month, day=28)

# Demo start = date(2026, 1, 28)

print("Daily (5 occurrences):") for d in daily_occurrences(start, 5): print(f" {d}")

print("\nWeekly on Monday (4 occurrences):") for d in weekly_occurrences(start, 4, weekday=0): print(f" {d} ({d.strftime('%A')})")

print("\nMonthly (6 occurrences):") for d in monthly_occurrences(start, 6): print(f" {d}")

Explanation

Run the file:

Daily (5 occurrences):
  2026-01-28
  2026-01-29
  2026-01-30
  2026-01-31
  2026-02-01

Weekly on Monday (4 occurrences): 2026-02-02 (Monday) 2026-02-09 (Monday) 2026-02-16 (Monday) 2026-02-23 (Monday)

Monthly (6 occurrences): 2026-01-28 2026-02-28 2026-03-28 2026-04-28 2026-05-28 2026-06-28

Patterns:

  • Daily: just add days
  • Weekly: calculate to target weekday, then add weeks
  • Monthly: replace month, handle edge cases

Why this matters

Recurring meetings, subscriptions, reminders — all need recurrence patterns.

āœ“ Checkpoint

⚠ If something breaks here

  • Monthly on 31st: some months don't have 31 days

What this page does

Calculates with business days (excluding weekends).

Where this fits

Business deadlines skip weekends.

Code (this page)

from datetime import date, timedelta

def add_business_days(start_date, num_days): """Add business days (Mon-Fri) to a date.""" current = start_date days_added = 0 while days_added < num_days: current += timedelta(days=1) if current.weekday() < 5: # Monday-Friday days_added += 1 return current

def business_days_between(start_date, end_date): """Count business days between two dates.""" count = 0 current = start_date while current < end_date: current += timedelta(days=1) if current.weekday() < 5: count += 1 return count

# Demo today = date.today() print(f"Today: {today} ({today.strftime('%A')})")

# Add 5 business days deadline = add_business_days(today, 5) print(f"5 business days later: {deadline} ({deadline.strftime('%A')})")

# Add 10 business days deadline2 = add_business_days(today, 10) print(f"10 business days later: {deadline2} ({deadline2.strftime('%A')})")

# Count business days in January jan_start = date(2026, 1, 1) jan_end = date(2026, 1, 31) work_days = business_days_between(jan_start, jan_end) print(f"\nBusiness days in January 2026: {work_days}")

Explanation

Run the file:

Today: 2026-01-28 (Wednesday)
5 business days later: 2026-02-04 (Wednesday)
10 business days later: 2026-02-11 (Wednesday)

Business days in January 2026: 22

Key points:

  • Skip weekends (weekday >= 5)
  • Could extend to skip holidays (with a holiday list)
  • Different from calendar days

Why this matters

"Delivery in 5 business days", "Payment due in 30 business days" — real deadlines.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. Could add holiday handling.

What this page does

Shows advanced timedelta operations.

Where this fits

Timedeltas can be combined and manipulated.

Code (this page)

from datetime import timedelta

# Basic arithmetic hour = timedelta(hours=1) day = timedelta(days=1)

print(f"1 day + 2 hours: {day + 2*hour}") print(f"1 day - 6 hours: {day - 6*hour}") print(f"3 days * 2: {3*day}") print(f"1 week / 2: {timedelta(weeks=1) / 2}")

# Total seconds week = timedelta(weeks=1) print(f"\n1 week = {week.total_seconds()} seconds") print(f"1 week = {week.total_seconds() / 3600} hours") print(f"1 week = {week.days} days")

# Comparisons short = timedelta(hours=1) long = timedelta(days=1) print(f"\n1 hour < 1 day: {short < long}") print(f"1 hour == 60 minutes: {short == timedelta(minutes=60)}")

# Absolute value negative = timedelta(days=-5) print(f"\nNegative duration: {negative}") print(f"Absolute value: {abs(negative)}")

Explanation

Run the file:

1 day + 2 hours: 1 day, 2:00:00
1 day - 6 hours: 18:00:00
3 days * 2: 6 days, 0:00:00
1 week / 2: 3 days, 12:00:00

1 week = 604800.0 seconds 1 week = 168.0 hours 1 week = 7 days

1 hour < 1 day: True 1 hour == 60 minutes: True

Negative duration: -5 days, 0:00:00 Absolute value: 5 days, 0:00:00

Timedelta supports:

  • Addition, subtraction
  • Multiplication, division by numbers
  • Comparison operators
  • abs() for absolute value

Why this matters

Complex duration calculations need these operations.

āœ“ Checkpoint

⚠ If something breaks here

  • Division: Only divide by numbers, not other timedeltas

What this page does

Uses the calendar module with datetime.

Where this fits

Calendar provides month/year views that complement datetime.

Code (this page)

import calendar
from datetime import date

# Print a month calendar print("January 2026:") print(calendar.month(2026, 1))

# Days in a month days_in_feb = calendar.monthrange(2026, 2)[1] print(f"Days in Feb 2026: {days_in_feb}")

# Is leap year? print(f"2024 is leap year: {calendar.isleap(2024)}") print(f"2026 is leap year: {calendar.isleap(2026)}")

# First and last day of month today = date.today() first_weekday, num_days = calendar.monthrange(today.year, today.month) first_day = today.replace(day=1) last_day = today.replace(day=num_days) print(f"\nThis month: {first_day} to {last_day}")

# All Sundays in a month print("\nSundays in January 2026:") cal = calendar.Calendar() for day in cal.itermonthdays2(2026, 1): date_num, weekday = day if date_num != 0 and weekday == 6: # 6 = Sunday print(f" January {date_num}")

Explanation

Run the file:

January 2026:
    January 2026
Mo Tu We Th Fr Sa Su
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Days in Feb 2026: 28 2024 is leap year: True 2026 is leap year: False

This month: 2026-01-01 to 2026-01-31

Sundays in January 2026: January 4 January 11 January 18 January 25

Calendar module helps with:

  • Month displays
  • Days in month
  • Leap year checking
  • Iterating specific weekdays

Why this matters

Calendar views, scheduling, and month-based logic benefit from the calendar module.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. Calendar is built-in.

What this page does

Validates date strings and values.

Where this fits

User input needs validation before use.

Code (this page)

from datetime import datetime, date

def is_valid_date(year, month, day): """Check if a date is valid.""" try: date(year, month, day) return True except ValueError: return False

def parse_date_safe(date_string, format="%Y-%m-%d"): """Parse date, return None if invalid.""" try: return datetime.strptime(date_string, format) except ValueError: return None

def validate_date_range(start, end): """Ensure start is before end.""" if start > end: raise ValueError("Start date must be before end date") return True

# Test validation print("Date validation:") print(f" 2026-02-28: {is_valid_date(2026, 2, 28)}") # Valid print(f" 2026-02-29: {is_valid_date(2026, 2, 29)}") # Invalid (not leap year) print(f" 2024-02-29: {is_valid_date(2024, 2, 29)}") # Valid (leap year) print(f" 2026-13-01: {is_valid_date(2026, 13, 1)}") # Invalid month

print("\nParsing:") print(f" '2026-01-28': {parse_date_safe('2026-01-28')}") print(f" 'invalid': {parse_date_safe('invalid')}") print(f" '28/01/2026': {parse_date_safe('28/01/2026')}") # Wrong format

print("\nRange validation:") try: validate_date_range(date(2026, 1, 1), date(2026, 12, 31)) print(" Valid range āœ“") except ValueError as e: print(f" Error: {e}")

Explanation

Run the file:

Date validation:
  2026-02-28: True
  2026-02-29: False
  2024-02-29: True
  2026-13-01: False

Parsing: '2026-01-28': 2026-01-28 00:00:00 'invalid': None '28/01/2026': None

Range validation: Valid range āœ“

Validation strategies:

  • Try to create the date (catch ValueError)
  • Return None on parse failure
  • Check logical constraints (start < end)

Why this matters

Invalid dates crash apps. Always validate user input.

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. These functions handle errors.

What this page does

Quick reference for all datetime classes and methods.

Where this fits

Lookup table after completing the guide.

Explanation

### Classes

ClassPurposeExample
dateDate onlydate(2026, 1, 28)
timeTime onlytime(14, 30)
datetimeDate + timedatetime(2026, 1, 28, 14, 30)
timedeltaDurationtimedelta(days=1)
timezoneTimezone offsettimezone.utc

### date Methods

MethodReturns
date.today()Current date
.year, .month, .dayComponents
.weekday()0-6 (Mon-Sun)
.isoformat()"YYYY-MM-DD"
.strftime(fmt)Formatted string

### datetime Methods

MethodReturns
datetime.now()Current datetime
datetime.utcnow()Current UTC
.date()Date part
.time()Time part
.timestamp()Unix timestamp
.strftime(fmt)Formatted string
.replace(...)New with changes

### Parsing

MethodInputOutput
strptime(s, fmt)Stringdatetime
fromisoformat(s)ISO stringdatetime
fromtimestamp(ts)Unix timedatetime

### Common Format Codes

CodeMeaning
%YYear 4-digit
%mMonth 01-12
%dDay 01-31
%HHour 00-23
%MMinute 00-59
%SSecond 00-59
%AWeekday name
%BMonth name

Why this matters

āœ“ Checkpoint

⚠ If something breaks here

Nothing to break. Reference only.

What this page does

Reusable code patterns for common tasks.

Where this fits

Copy-paste solutions for frequent needs.

Code (this page)

from datetime import datetime, date, timedelta, timezone

# Pattern 1: Today at midnight today_midnight = datetime.combine(date.today(), datetime.min.time())

# Pattern 2: End of today today_end = datetime.combine(date.today(), datetime.max.time())

# Pattern 3: First day of month first_of_month = date.today().replace(day=1)

# Pattern 4: Days until date target = date(2026, 12, 25) days_until = (target - date.today()).days

# Pattern 5: Is date in past? is_past = date(2020, 1, 1) < date.today()

# Pattern 6: Format for display display = datetime.now().strftime("%B %d, %Y at %I:%M %p")

# Pattern 7: Parse ISO date dt = datetime.fromisoformat("2026-01-28T14:30:00")

# Pattern 8: Current UTC timestamp utc_now = datetime.now(timezone.utc)

# Pattern 9: Add months (approximate) def add_months(dt, months): month = dt.month + months year = dt.year + (month - 1) // 12 month = (month - 1) % 12 + 1 day = min(dt.day, [31,28,31,30,31,30,31,31,30,31,30,31][month-1]) return dt.replace(year=year, month=month, day=day)

# Pattern 10: Time since event event_time = datetime(2026, 1, 1, 0, 0, 0) time_since = datetime.now() - event_time

print("Patterns demonstrated successfully!") print(f"Today midnight: {today_midnight}") print(f"Days until Christmas: {days_until}") print(f"Display format: {display}")

Explanation

These patterns solve common problems:

  • Midnight and end-of-day boundaries
  • Month boundaries
  • Date comparisons
  • Formatting for users
  • Safe parsing
  • Adding months (tricky!)

Why this matters

āœ“ Checkpoint

⚠ If something breaks here

Nothing should break. These are proven patterns.

What this page does

Confirms you have mastered all skills in this guide.

Where this fits

This is the end. Verify everything works together.

Explanation

Complete this final test in your datetime_basics.py:

from datetime import datetime, date, timedelta, timezone

print("=== DateTime Module Final Test ===\n")

# 1. Current date and time now = datetime.now() print(f"1. Now: {now.strftime('%Y-%m-%d %H:%M:%S')}")

# 2. Create specific date birthday = date(1990, 6, 15) age = date.today().year - birthday.year print(f"2. Born {birthday}, age ~{age}")

# 3. Date arithmetic deadline = date.today() + timedelta(days=30) print(f"3. 30 days from now: {deadline}")

# 4. Parse and format date_str = "December 25, 2026" christmas = datetime.strptime(date_str, "%B %d, %Y") print(f"4. Parsed: '{date_str}' → {christmas.date()}")

# 5. Time until event remaining = christmas - datetime.now() print(f"5. Days until Christmas: {remaining.days}")

# 6. Timezone aware utc_now = datetime.now(timezone.utc) print(f"6. UTC time: {utc_now.strftime('%H:%M:%S %Z')}")

# 7. Compare dates if date.today() < christmas.date(): print("7. Christmas is coming!") else: print("7. Christmas has passed")

# 8. Weekday print(f"8. Christmas 2026 is on a {christmas.strftime('%A')}")

print("\nāœ“ DateTime Module Fundamentals Complete!")

Run it. If you understand every line, you've completed the guide.

Why this matters

You now master dates and times in Python: creating, formatting, parsing, calculating, and comparing.

āœ“ Checkpoint

Contents