Marcus Kazmierczak

Home mkaz.blog

Working With Python

Working with Dates and Times in Python

A comprehensive guide to Python date and time handling, including formatting dates, date calculations, timezone handling, and troubleshooting common issues. This guide covers everything from basic date creation to advanced business date calculations.

Quick Start / TL;DR

  • Basic imports: from datetime import datetime, date, timedelta
  • Create current date: datetime.now() or date.today()
  • Parse date string: datetime.strptime("2023-12-25", "%Y-%m-%d")
  • Format for display: dt.strftime("%B %d, %Y") # December 25, 2023
  • Date arithmetic: dt + timedelta(days=7) # Add 7 days
  • Timezone-aware: Use zoneinfo for proper timezone handling

Common Questions

How do I work with dates and times in Python?

Python provides several built-in modules for date and time handling:

from datetime import datetime, date, time, timedelta
from zoneinfo import ZoneInfo

# Current date and time
now = datetime.now()
today = date.today()
current_time = time(14, 30, 0)  # 2:30 PM

print(f"Now: {now}")
print(f"Today: {today}")
print(f"Time: {current_time}")

Output:

Now: 2025-09-19 14:30:45.123456
Today: 2025-09-19
Time: 14:30:00

What's the difference between datetime, date, and time objects?

Here's a comparison of Python's core date/time types:

Type Purpose Example Use Case
datetime Full date + time 2025-09-19 14:30:45 Timestamps, logging, precise scheduling
date Date only 2025-09-19 Birthdays, deadlines, calendar events
time Time only 14:30:45 Daily schedules, recurring times
timedelta Duration/difference 7 days, 2:30:45 Date arithmetic, intervals

Examples of each type

from datetime import datetime, date, time, timedelta

dt = datetime(2025, 9, 19, 14, 30, 45)  # Full datetime
d = date(2025, 9, 19)                   # Date only
t = time(14, 30, 45)                    # Time only
td = timedelta(days=7, hours=2)         # Duration

print(f"DateTime: {dt}")
print(f"Date: {d}")
print(f"Time: {t}")
print(f"TimeDelta: {td}")

# Convert between types
date_from_datetime = dt.date()  # Extract date
time_from_datetime = dt.time()  # Extract time
datetime_from_parts = datetime.combine(d, t)  # Combine date + time

Output:

DateTime: 2025-09-19 14:30:45
Date: 2025-09-19
Time: 14:30:45
TimeDelta: 7 days, 2:00:00

How do I parse date strings in Python?

For known formats, use strptime(), which takes two arguments: the date string and a format string describing its structure (using special codes like %Y for year, %m for month, %d for day, etc). This lets you precisely parse dates when you know their exact format:

from datetime import datetime

# Examples using known formats
dt = datetime.strptime("2025-09-19", "%Y-%m-%d")
dt = datetime.strptime("09/19/2025", "%m/%d/%Y")
dt = datetime.strptime("Sep 19, 2025", "%b %d, %Y")
dt = datetime.strptime("September 19, 2025", "%B %d, %Y")
dt = datetime.strptime("2025-09-19 14:30:45", "%Y-%m-%d %H:%M:%S")

For unknown formats, use the dateutil library:

import dateutil.parser as parser

# These all work without specifying format
date_strings = [
    "October 11, 2025",
    "Oct 11, 2025",
    "Oct 11th, 2025",
    "2025-10-11T14:30:00",
    "next tuesday",
    "tomorrow at 3pm"
]

for date_string in date_strings:
    try:
        parsed_date = parser.parse(date_string)
        print(f"'{date_string}' → {parsed_date}")
    except parser.ParserError as e:
        print(f"Could not parse '{date_string}': {e}")

How do I format dates for display?

Use strftime() for custom formatting:

from datetime import datetime

dt = datetime(2025, 9, 19, 14, 30, 45)

# Common display formats
formats = {
    "ISO format": dt.strftime("%Y-%m-%d"),
    "US format": dt.strftime("%m/%d/%Y"),
    "Long format": dt.strftime("%B %d, %Y"),
    "Short format": dt.strftime("%b %d, %Y"),
    "With time": dt.strftime("%Y-%m-%d %H:%M:%S"),
    "12-hour time": dt.strftime("%Y-%m-%d %I:%M:%S %p"),
    "Weekday": dt.strftime("%A, %B %d, %Y"),
    "RFC format": dt.strftime("%a, %d %b %Y %H:%M:%S"),
}

for name, formatted in formats.items():
    print(f"{name:12}: {formatted}")

Output:

ISO format  : 2025-09-19
US format   : 09/19/2025
Long format : September 19, 2025
Short format: Sep 19, 2025
With time   : 2025-09-19 14:30:45
12-hour time: 2025-09-19 02:30:45 PM
Weekday     : Friday, September 19, 2025
RFC format  : Fri, 19 Sep 2025 14:30:45

How do I calculate the difference between two dates?

Use date arithmetic to get timedelta objects:

from datetime import datetime, date, timedelta

# Calculate age
birth_date = date(1990, 5, 15)
today = date.today()
age_delta = today - birth_date

print(f"Age: {age_delta.days} days")
print(f"Age in years: {age_delta.days // 365.25:.1f} years")

# Project deadlines
project_start = datetime(2025, 9, 1, 9, 0)
project_end = datetime(2025, 12, 15, 17, 0)
project_duration = project_end - project_start

print(f"Project duration: {project_duration}")
print(f"Working days: {project_duration.days}")
print(f"Total hours: {project_duration.total_seconds() / 3600:.0f} hours")

# Business days calculation (excluding weekends)
def business_days_between(start_date, end_date):
    """Calculate business days between two dates."""
    current = start_date
    business_days = 0
    while current <= end_date:
        if current.weekday() < 5:  # Monday=0, Sunday=6
            business_days += 1
        current += timedelta(days=1)
    return business_days

start = date(2025, 9, 15)  # Monday
end = date(2025, 9, 26)    # Friday
biz_days = business_days_between(start, end)
print(f"Business days: {biz_days}")

What are timezone-aware vs naive datetimes?

Naive datetimes have no timezone information:

from datetime import datetime

# Naive datetime - no timezone info
naive_dt = datetime(2025, 9, 19, 14, 30)
print(f"Naive: {naive_dt} (tzinfo: {naive_dt.tzinfo})")

Timezone-aware datetimes include timezone information:

from datetime import datetime, timezone
from zoneinfo import ZoneInfo  # Python 3.9+

# UTC timezone
utc_dt = datetime(2025, 9, 19, 14, 30, tzinfo=timezone.utc)
print(f"UTC: {utc_dt}")

# Specific timezones using zoneinfo
ny_dt = datetime(2025, 9, 19, 14, 30, tzinfo=ZoneInfo("America/New_York"))
tokyo_dt = datetime(2025, 9, 19, 14, 30, tzinfo=ZoneInfo("Asia/Tokyo"))

print(f"New York: {ny_dt}")
print(f"Tokyo: {tokyo_dt}")

# Convert between timezones
ny_time = utc_dt.astimezone(ZoneInfo("America/New_York"))
print(f"UTC {utc_dt.time()} → NY {ny_time.time()}")

# Current time in different timezones
now_utc = datetime.now(timezone.utc)
now_ny = now_utc.astimezone(ZoneInfo("America/New_York"))
now_tokyo = now_utc.astimezone(ZoneInfo("Asia/Tokyo"))

print(f"UTC: {now_utc.strftime('%H:%M')}")
print(f"NY: {now_ny.strftime('%H:%M')}")
print(f"Tokyo: {now_tokyo.strftime('%H:%M')}")

Creating Date Objects

First off, all basic examples use the following import, any additional imports needed will be shown with the example.

from datetime import datetime

How do I create a specific date?

from datetime import datetime, date, time

# Current date/time
now = datetime.now()
today = date.today()

# Specific dates - multiple ways
dt1 = datetime(2025, 8, 28)
dt2 = datetime(year=2025, month=3, day=2)
dt3 = datetime(2025, 8, 28, 14, 30, 45)  # With time

# Date only
date1 = date(2025, 8, 28)

# Time only
time1 = time(14, 30, 45)

print(f"Now: {now}")
print(f"Today: {today}")
print(f"Specific datetime: {dt3}")
print(f"Date only: {date1}")
print(f"Time only: {time1}")

How do I create a date from a timestamp?

Create a Python date object from a unix timestamp (seconds since epoch):

import time as time_module

# Current timestamp
current_timestamp = time_module.time()
print(f"Current timestamp: {current_timestamp}")

# Specific timestamp
timestamp = 1294204471
dt = datetime.fromtimestamp(timestamp)
print(f"Timestamp {timestamp}{dt}")

# Timezone-aware from timestamp
utc_dt = datetime.fromtimestamp(timestamp, tz=timezone.utc)
print(f"UTC: {utc_dt}")

# Convert datetime back to timestamp
back_to_timestamp = dt.timestamp()
print(f"Back to timestamp: {back_to_timestamp}")

Date Format Reference Table

Printing dates in various formats is straightforward using strftime(). Here's the complete reference:

dt = datetime(2025, 8, 22, 14, 30, 45, 123456)
print(f"Example date: {dt}")
print(f"Formatted: {dt.strftime('%b %d, %Y at %I:%M %p')}")
# Output: Aug 22, 2025 at 02:30 PM
Symbol Definition Example Usage
Date Components
%Y Year with century 2025 Full year
%y Year without century 25 Two-digit year
%m Month as number 08 Numeric month [01-12]
%B Full month name August Full month name
%b Abbreviated month Aug Short month name
%d Day of month 22 Day [01-31]
Time Components
%H Hour (24-hour) 14 Military time [00-23]
%I Hour (12-hour) 02 Standard time [01-12]
%M Minute 30 Minutes [00-59]
%S Second 45 Seconds [00-59]
%f Microsecond 123456 Microseconds [000000-999999]
%p AM/PM PM Locale's AM/PM
Weekday Components
%A Full weekday name Friday Full day name
%a Abbreviated weekday Fri Short day name
%w Weekday as number 5 Sunday=0 to Saturday=6
Week/Year Components
%j Day of year 234 Julian day [001-366]
%U Week number (Sunday first) 33 Week of year [00-53]
%W Week number (Monday first) 33 Week of year [00-53]
Timezone Components
%z UTC offset +0000 ±HHMM or empty
%Z Timezone name UTC Time zone name
Locale Components
%c Complete date/time Fri Aug 22 14:30:45 2025 Locale's datetime
%x Locale date 08/22/25 Locale's date
%X Locale time 14:30:45 Locale's time
Literal
%% Literal % % Percent character

Common Format Examples

dt = datetime(2025, 8, 22, 14, 30, 45)

common_formats = {
    "ISO 8601": dt.strftime("%Y-%m-%d"),                    # 2025-08-22
    "US Date": dt.strftime("%m/%d/%Y"),                     # 08/22/2025
    "European": dt.strftime("%d/%m/%Y"),                    # 22/08/2025
    "Long Format": dt.strftime("%B %d, %Y"),               # August 22, 2025
    "With Weekday": dt.strftime("%A, %B %d, %Y"),          # Friday, August 22, 2025
    "Short": dt.strftime("%b %d"),                          # Aug 22
    "Time 24h": dt.strftime("%H:%M:%S"),                    # 14:30:45
    "Time 12h": dt.strftime("%I:%M:%S %p"),                 # 02:30:45 PM
    "DateTime": dt.strftime("%Y-%m-%d %H:%M:%S"),           # 2025-08-22 14:30:45
    "RFC 2822": dt.strftime("%a, %d %b %Y %H:%M:%S"),       # Fri, 22 Aug 2025 14:30:45
    "Log Format": dt.strftime("[%Y-%m-%d %H:%M:%S]"),       # [2025-08-22 14:30:45]
}

for name, formatted in common_formats.items():
    print(f"{name:12}: {formatted}")

How do I format dates for specific standards?

RFC 2822 Format (Email Headers)

Use the formatdate() function from the email.utils built-in module.

For the current date and time:

from email.utils import formatdate

current_rfc = formatdate()
print(f"Current RFC 2822: {current_rfc}")
# Output: 'Thu, 19 Sep 2025 14:30:44 +0000'

If you need a specific date to format, pass in a unix timestamp:

from email.utils import formatdate
from datetime import datetime

dt = datetime(2025, 1, 25, 12, 0, 0)
rfc_formatted = formatdate(dt.timestamp())
print(f"Specific RFC 2822: {rfc_formatted}")
# Output: 'Sat, 25 Jan 2025 12:00:00 +0000'

ISO 8601 Format

from datetime import datetime, timezone

dt = datetime(2025, 9, 19, 14, 30, 45, 123456)

# Various ISO formats
iso_formats = {
    "Date only": dt.date().isoformat(),                    # 2025-09-19
    "DateTime": dt.isoformat(),                            # 2025-09-19T14:30:45.123456
    "DateTime (no microseconds)": dt.replace(microsecond=0).isoformat(),  # 2025-09-19T14:30:45
    "With timezone": dt.replace(tzinfo=timezone.utc).isoformat(),  # 2025-09-19T14:30:45.123456+00:00
}

for name, formatted in iso_formats.items():
    print(f"{name:25}: {formatted}")

Date Calculations and Timedelta

How do I add or subtract time from dates?

Use timedelta for date arithmetic:

from datetime import datetime, timedelta

dt = datetime(2025, 9, 19, 14, 30, 45)

# Adding time
future_dates = {
    "1 week later": dt + timedelta(weeks=1),
    "7 days later": dt + timedelta(days=7),
    "5 hours later": dt + timedelta(hours=5),
    "30 minutes later": dt + timedelta(minutes=30),
    "45 seconds later": dt + timedelta(seconds=45),
    "1000 microseconds later": dt + timedelta(microseconds=1000),
}

# Subtracting time
past_dates = {
    "1 week ago": dt - timedelta(weeks=1),
    "7 days ago": dt - timedelta(days=7),
    "5 hours ago": dt - timedelta(hours=5),
}

print(f"Base date: {dt}")
print("\nFuture dates:")
for description, future_dt in future_dates.items():
    print(f"  {description:20}: {future_dt}")

print("\nPast dates:")
for description, past_dt in past_dates.items():
    print(f"  {description:20}: {past_dt}")

Valid timedelta properties are: weeks, days, hours, minutes, seconds, microseconds, milliseconds

Why should I avoid using days=365 for year calculations?

Don't be tempted to use days=365 for year calculations—this would be incorrect for leap years. Instead, use proper date construction:

from datetime import datetime, timedelta

# ❌ WRONG - doesn't account for leap years
start_date = datetime(2024, 2, 29)  # Leap year date
wrong_next_year = start_date + timedelta(days=365)
print(f"Wrong approach: {wrong_next_year}")  # 2025-02-28

# ✅ CORRECT - handles leap years properly
correct_next_year = datetime(year=start_date.year + 1,
                           month=start_date.month,
                           day=start_date.day)
print(f"Correct approach: {correct_next_year}")  # 2025-02-28

# ✅ BETTER - handle leap year edge case
def add_years(dt, years):
    """Add years to a datetime, handling leap year edge cases."""
    try:
        return dt.replace(year=dt.year + years)
    except ValueError:
        # Handle Feb 29 on non-leap year
        return dt.replace(year=dt.year + years, day=28)

leap_day = datetime(2024, 2, 29)
next_year = add_years(leap_day, 1)
print(f"Leap year handling: {next_year}")  # 2025-02-28

How do I calculate the difference between two dates?

When you subtract date objects, they return timedelta objects:

from datetime import datetime, date

# DateTime calculations
dt1 = datetime(2025, 8, 23, 9, 30)
dt2 = datetime(2025, 8, 28, 17, 45)
time_diff = dt2 - dt1

print(f"Difference: {time_diff}")
print(f"Days: {time_diff.days}")
print(f"Seconds: {time_diff.seconds}")
print(f"Total seconds: {time_diff.total_seconds()}")
print(f"Total hours: {time_diff.total_seconds() / 3600:.1f}")

# Date-only calculations
date1 = date(2025, 1, 1)
date2 = date(2025, 12, 31)
date_diff = date2 - date1

print(f"Days in 2025: {date_diff.days}")

# Age calculation
birth_date = date(1990, 5, 15)
today = date.today()
age = today - birth_date
age_years = age.days / 365.25  # Account for leap years

print(f"Age: {age_years:.1f} years")

Output:

Difference: 5 days, 8:15:00
Days: 5
Seconds: 29700
Total seconds: 461700.0
Total hours: 128.2
Days in 2025: 364
Age: 35.4 years

Converting Between Date Types

How do I convert datetime to date?

To simplify a datetime to just the date, use the .date() method:

from datetime import datetime

dt = datetime.now()
print(f"DateTime: {dt}")
print(f"Date only: {dt.date()}")
print(f"Time only: {dt.time()}")

# Practical example: group events by date
events = [
    datetime(2025, 9, 19, 9, 30),
    datetime(2025, 9, 19, 14, 15),
    datetime(2025, 9, 20, 10, 0),
]

events_by_date = {}
for event in events:
    event_date = event.date()
    if event_date not in events_by_date:
        events_by_date[event_date] = []
    events_by_date[event_date].append(event.time())

for date, times in events_by_date.items():
    print(f"{date}: {times}")

How do I convert date to datetime?

To add a time to a date, use datetime.combine() to merge a date object with a time object:

from datetime import date, datetime, time

# Combine date and time
today = date.today()
morning_time = time(9, 0, 0)
afternoon_time = time(17, 30, 0)

morning_datetime = datetime.combine(today, morning_time)
afternoon_datetime = datetime.combine(today, afternoon_time)

print(f"Morning meeting: {morning_datetime}")
print(f"End of day: {afternoon_datetime}")

# Common use case: create datetime for start/end of day
def start_of_day(date_obj):
    """Get datetime for start of day (00:00:00)."""
    return datetime.combine(date_obj, time.min)

def end_of_day(date_obj):
    """Get datetime for end of day (23:59:59.999999)."""
    return datetime.combine(date_obj, time.max)

today = date.today()
print(f"Day starts: {start_of_day(today)}")
print(f"Day ends: {end_of_day(today)}")

Common Date Functions and Use Cases

Here are examples of commonly needed Python date functions with real-world applications.

How do I get yesterday's date?

from datetime import date, datetime, timedelta

# Yesterday as date
yesterday_date = date.today() - timedelta(days=1)
print(f"Yesterday: {yesterday_date.strftime('%Y-%m-%d')}")

# Yesterday as datetime
yesterday_datetime = datetime.now() - timedelta(days=1)
print(f"Yesterday (exact time): {yesterday_datetime}")

# Practical example: log file names
log_filename = f"app_{yesterday_date.strftime('%Y%m%d')}.log"
print(f"Yesterday's log file: {log_filename}")

How do I find the last day of the month?

Two reliable solutions:

Method 1: Using next month minus a day

from datetime import datetime, timedelta

def last_day_of_month_method1(dt):
    """Find last day of month by going to first day of next month."""
    if dt.month == 12:
        next_month = datetime(year=dt.year + 1, month=1, day=1)
    else:
        next_month = datetime(year=dt.year, month=dt.month + 1, day=1)
    return next_month - timedelta(days=1)

now = datetime.now()
last_day = last_day_of_month_method1(now)
print(f"Last day of {now.strftime('%B')}: {last_day.date()}")

Method 2: Using the calendar module

import calendar
from datetime import datetime

def last_day_of_month_method2(dt):
    """Find last day of month using calendar module."""
    _, last_day_num = calendar.monthrange(dt.year, dt.month)
    return dt.replace(day=last_day_num)

now = datetime.now()
last_day = last_day_of_month_method2(now)
print(f"Last day of month: {last_day.date()}")

# Check if it's a leap year
def is_leap_year(year):
    return calendar.isleap(year)

print(f"Is 2025 a leap year? {is_leap_year(2025)}")
print(f"Days in February 2025: {calendar.monthrange(2025, 2)[1]}")

How do I find the next occurrence of a specific weekday?

from datetime import datetime, timedelta

def next_weekday(weekday):
    """
    Find the next occurrence of a specific weekday.
    weekday: 0=Monday, 1=Tuesday, ..., 6=Sunday
    """
    today = datetime.now()
    days_ahead = weekday - today.weekday()
    if days_ahead <= 0:  # Target day already happened this week
        days_ahead += 7
    return today + timedelta(days=days_ahead)

# Find next Thursday (weekday 3)
next_thursday = next_weekday(3)
print(f"Next Thursday: {next_thursday.strftime('%A, %B %d, %Y')}")

# More readable version with names
weekday_names = {
    'monday': 0, 'tuesday': 1, 'wednesday': 2, 'thursday': 3,
    'friday': 4, 'saturday': 5, 'sunday': 6
}

def next_named_weekday(day_name):
    """Find next occurrence of named weekday."""
    return next_weekday(weekday_names[day_name.lower()])

next_friday = next_named_weekday('friday')
print(f"Next Friday: {next_friday.strftime('%A, %B %d, %Y')}")

How do I find the first Monday of the month?

from datetime import datetime, timedelta

def first_monday_of_month(year, month):
    """Find the first Monday of a specific month."""
    # First day of the month
    first_day = datetime(year, month, 1)

    # Find how many days until Monday (0 = Monday)
    days_until_monday = (7 - first_day.weekday()) % 7
    if days_until_monday == 0 and first_day.weekday() != 0:
        days_until_monday = 7

    first_monday = first_day + timedelta(days=days_until_monday)
    return first_monday

# Current month's first Monday
today = datetime.now()
first_monday = first_monday_of_month(today.year, today.month)
print(f"First Monday of {today.strftime('%B')}: {first_monday.date()}")

# More general solution for any weekday
def first_weekday_of_month(year, month, weekday):
    """
    Find the first occurrence of a weekday in a month.
    weekday: 0=Monday, 1=Tuesday, ..., 6=Sunday
    """
    first_day = datetime(year, month, 1)
    first_weekday_day = first_day.weekday()

    days_to_add = (weekday - first_weekday_day) % 7
    return first_day + timedelta(days=days_to_add)

# Examples
first_wednesday = first_weekday_of_month(2025, 9, 2)  # Wednesday
print(f"First Wednesday of September 2025: {first_wednesday.date()}")

How do I work with business days and holidays?

from datetime import date, timedelta

def is_weekend(dt):
    """Check if date falls on weekend."""
    return dt.weekday() >= 5  # Saturday=5, Sunday=6

def add_business_days(start_date, business_days):
    """Add business days to a date, skipping weekends."""
    current_date = start_date
    days_added = 0

    while days_added < business_days:
        current_date += timedelta(days=1)
        if not is_weekend(current_date):
            days_added += 1

    return current_date

def business_days_between(start_date, end_date):
    """Count business days between two dates."""
    current = start_date
    business_days = 0

    while current <= end_date:
        if not is_weekend(current):
            business_days += 1
        current += timedelta(days=1)

    return business_days

# Examples
today = date.today()
five_biz_days_later = add_business_days(today, 5)
print(f"5 business days from {today}: {five_biz_days_later}")

# Count business days in a period
start = date(2025, 9, 1)  # Monday
end = date(2025, 9, 30)   # Tuesday
biz_days = business_days_between(start, end)
print(f"Business days in September 2025: {biz_days}")

# Simple holiday handling
US_HOLIDAYS_2025 = [
    date(2025, 1, 1),   # New Year's Day
    date(2025, 7, 4),   # Independence Day
    date(2025, 12, 25), # Christmas Day
]

def is_holiday(dt, holidays=US_HOLIDAYS_2025):
    """Check if date is a holiday."""
    return dt in holidays

def is_business_day(dt):
    """Check if date is a business day (not weekend or holiday)."""
    return not is_weekend(dt) and not is_holiday(dt)

test_date = date(2025, 7, 4)  # July 4th, 2025 (Friday)
print(f"Is {test_date} a business day? {is_business_day(test_date)}")

Advanced Date and Time Operations

How do I handle timezone conversions?

Using zoneinfo:

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

# Create timezone-aware datetimes
utc_now = datetime.now(timezone.utc)
ny_time = datetime.now(ZoneInfo("America/New_York"))
london_time = datetime.now(ZoneInfo("Europe/London"))
tokyo_time = datetime.now(ZoneInfo("Asia/Tokyo"))

print(f"UTC: {utc_now.strftime('%Y-%m-%d %H:%M:%S %Z')}")
print(f"New York: {ny_time.strftime('%Y-%m-%d %H:%M:%S %Z')}")
print(f"London: {london_time.strftime('%Y-%m-%d %H:%M:%S %Z')}")
print(f"Tokyo: {tokyo_time.strftime('%Y-%m-%d %H:%M:%S %Z')}")

# Convert between timezones
meeting_utc = datetime(2025, 9, 19, 15, 0, tzinfo=timezone.utc)
meeting_ny = meeting_utc.astimezone(ZoneInfo("America/New_York"))
meeting_tokyo = meeting_utc.astimezone(ZoneInfo("Asia/Tokyo"))

print(f"Meeting times:")
print(f"  UTC: {meeting_utc.strftime('%H:%M')}")
print(f"  New York: {meeting_ny.strftime('%H:%M')}")
print(f"  Tokyo: {meeting_tokyo.strftime('%H:%M')}")

How do I work with date ranges and iterations?

from datetime import date, datetime, timedelta

def date_range(start_date, end_date, step_days=1):
    """Generate dates between start and end date."""
    current = start_date
    while current <= end_date:
        yield current
        current += timedelta(days=step_days)

# Example: All dates in September 2025
start = date(2025, 9, 1)
end = date(2025, 9, 30)

print("All Mondays in September 2025:")
for dt in date_range(start, end):
    if dt.weekday() == 0:  # Monday
        print(f"  {dt.strftime('%A, %B %d')}")

# Weekly iterations
print("\nEvery week in September:")
for dt in date_range(start, end, 7):
    print(f"  Week starting: {dt}")

# Business days only
def business_date_range(start_date, end_date):
    """Generate only business days (Mon-Fri) between dates."""
    for dt in date_range(start_date, end_date):
        if dt.weekday() < 5:  # Monday=0 to Friday=4
            yield dt

print("\nFirst 5 business days in September:")
for i, dt in enumerate(business_date_range(start, end)):
    if i >= 5:
        break
    print(f"  {dt.strftime('%A, %B %d')}")

Troubleshooting Common Date/Time Issues

What are the most common datetime parsing errors?

1. Format Mismatch Errors:

from datetime import datetime

# ❌ Common mistake: wrong format string
try:
    # This will fail - format doesn't match
    dt = datetime.strptime("2025-09-19", "%m/%d/%Y")
except ValueError as e:
    print(f"Format mismatch error: {e}")

# ✅ Correct approach: match format to string
dt = datetime.strptime("2025-09-19", "%Y-%m-%d")
print(f"Correctly parsed: {dt}")

# ✅ Better: Handle multiple formats
def safe_parse_date(date_string):
    formats = ["%Y-%m-%d", "%m/%d/%Y", "%d/%m/%Y", "%B %d, %Y"]

    for fmt in formats:
        try:
            return datetime.strptime(date_string, fmt)
        except ValueError:
            continue

    raise ValueError(f"No matching format found for: {date_string}")

# Test with different formats
test_dates = ["2025-09-19", "09/19/2025", "September 19, 2025"]
for date_str in test_dates:
    parsed = safe_parse_date(date_str)
    print(f"'{date_str}' → {parsed}")

2. Timezone-Related Errors:

from datetime import datetime, timezone

# ❌ Mixing naive and timezone-aware datetimes
naive_dt = datetime(2025, 9, 19, 14, 30)
aware_dt = datetime(2025, 9, 19, 14, 30, tzinfo=timezone.utc)

try:
    # This will raise TypeError
    difference = aware_dt - naive_dt
except TypeError as e:
    print(f"Timezone error: {e}")

# ✅ Convert naive to aware before operations
naive_as_utc = naive_dt.replace(tzinfo=timezone.utc)
difference = aware_dt - naive_as_utc
print(f"Time difference: {difference}")

# ✅ Better: Always use timezone-aware datetimes
def now_utc():
    return datetime.now(timezone.utc)

def make_aware(dt, tz=timezone.utc):
    """Convert naive datetime to timezone-aware."""
    if dt.tzinfo is None:
        return dt.replace(tzinfo=tz)
    return dt

3. Leap Year and Month-End Issues:

from datetime import datetime, timedelta
import calendar

def safe_add_months(dt, months):
    """Safely add months, handling month-end edge cases."""
    target_month = dt.month + months
    target_year = dt.year + (target_month - 1) // 12
    target_month = ((target_month - 1) % 12) + 1

    # Handle day overflow (e.g., Jan 31 + 1 month)
    max_day = calendar.monthrange(target_year, target_month)[1]
    target_day = min(dt.day, max_day)

    return dt.replace(year=target_year, month=target_month, day=target_day)

# Examples of edge cases
edge_cases = [
    datetime(2025, 1, 31),  # Jan 31 + 1 month = Feb 28
    datetime(2024, 2, 29),  # Leap day + 1 year = Feb 28
    datetime(2025, 8, 31),  # Aug 31 + 1 month = Sep 30
]

for dt in edge_cases:
    next_month = safe_add_months(dt, 1)
    print(f"{dt.date()} + 1 month = {next_month.date()}")

How do I debug date formatting issues?

from datetime import datetime

def debug_date_format(dt, format_string):
    """Debug date formatting by showing each component."""
    print(f"Original datetime: {dt}")
    print(f"Format string: '{format_string}'")

    try:
        formatted = dt.strftime(format_string)
        print(f"Result: '{formatted}'")

        # Break down format components
        format_parts = [
            ('%Y', 'Full year'),
            ('%y', '2-digit year'),
            ('%m', 'Month (01-12)'),
            ('%B', 'Full month name'),
            ('%b', 'Short month name'),
            ('%d', 'Day (01-31)'),
            ('%H', 'Hour 24h (00-23)'),
            ('%I', 'Hour 12h (01-12)'),
            ('%M', 'Minute (00-59)'),
            ('%S', 'Second (00-59)'),
            ('%p', 'AM/PM'),
            ('%A', 'Full weekday'),
            ('%a', 'Short weekday'),
        ]

        print("Components:")
        for code, desc in format_parts:
            if code in format_string:
                component_value = dt.strftime(code)
                print(f"  {code} ({desc}): '{component_value}'")

        return formatted

    except ValueError as e:
        print(f"Error: {e}")
        return None

# Example debugging session
dt = datetime(2025, 9, 19, 14, 30, 45)
debug_date_format(dt, "%Y-%m-%d %I:%M %p")

How do I handle date validation in user input?

from datetime import datetime, date
import re

class DateValidator:
    """Comprehensive date validation with helpful error messages."""

    def __init__(self):
        self.formats = [
            ("%Y-%m-%d", "YYYY-MM-DD"),
            ("%m/%d/%Y", "MM/DD/YYYY"),
            ("%d/%m/%Y", "DD/MM/YYYY"),
            ("%B %d, %Y", "Month DD, YYYY"),
            ("%b %d, %Y", "Mon DD, YYYY"),
        ]

    def validate_date_string(self, date_string, min_date=None, max_date=None):
        """Validate and parse date string with comprehensive error handling."""

        if not date_string or not date_string.strip():
            return False, "Date cannot be empty"

        date_string = date_string.strip()

        # Try parsing with known formats
        parsed_date = None
        for format_code, format_name in self.formats:
            try:
                parsed_date = datetime.strptime(date_string, format_code)
                break
            except ValueError:
                continue

        if not parsed_date:
            format_examples = [name for _, name in self.formats]
            return False, f"Invalid date format. Try: {', '.join(format_examples)}"

        # Validate date range
        if min_date and parsed_date.date() < min_date:
            return False, f"Date must be after {min_date}"

        if max_date and parsed_date.date() > max_date:
            return False, f"Date must be before {max_date}"

        # Additional validations
        current_year = datetime.now().year
        if parsed_date.year < 1900 or parsed_date.year > current_year + 100:
            return False, f"Year must be between 1900 and {current_year + 100}"

        return True, parsed_date.date()

    def validate_age(self, birth_date_string, min_age=0, max_age=150):
        """Validate birth date and calculate age."""
        is_valid, result = self.validate_date_string(
            birth_date_string,
            max_date=date.today()  # Can't be in future
        )

        if not is_valid:
            return False, result

        birth_date = result
        today = date.today()
        age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))

        if age < min_age:
            return False, f"Age must be at least {min_age}"

        if age > max_age:
            return False, f"Age cannot exceed {max_age}"

        return True, {"birth_date": birth_date, "age": age}

# Example usage
validator = DateValidator()

test_inputs = [
    "2025-09-19",
    "09/19/2025",
    "September 19, 2025",
    "invalid date",
    "2030-01-01",
    "1990-05-15",
]

print("Date validation examples:")
for test_input in test_inputs:
    is_valid, result = validator.validate_date_string(
        test_input,
        min_date=date(2020, 1, 1),
        max_date=date(2025, 12, 31)
    )

    if is_valid:
        print(f"✓ '{test_input}' → {result}")
    else:
        print(f"✗ '{test_input}' → {result}")

print("\nAge validation examples:")
birth_dates = ["1990-05-15", "2010-01-01", "1850-01-01"]
for birth_date in birth_dates:
    is_valid, result = validator.validate_age(birth_date, min_age=18)
    if is_valid:
        print(f"✓ Born {birth_date} → Age {result['age']}")
    else:
        print(f"✗ Born {birth_date}{result}")

Reference