Date/Time Formatting and Manipulation
You've built datetime objects from user input and understand the basics of timezone awareness. Now you'll learn how to format those objects for display and manipulate them to solve real-world scheduling problems. By the end of this lesson, you'll format dates for different audiences, calculate durations, and convert times across timezones—the exact skills you need for building global applications.
Why Formatting Matters
Think about your favorite app. When it shows you an event time, it doesn't say "2025-11-09T14:30:00+00:00" (that's for computers). It says something like "Sunday, November 9 at 2:30 PM" or "2025-11-09" depending on context. The raw datetime object is useless to humans without formatting.
Similarly, when you're building a scheduling feature or calculating project deadlines, you need to manipulate dates—add days, subtract hours, find the difference between two moments. That's where timedelta comes in.
And when your app serves users around the world, you need to convert between timezones correctly. A meeting at 9 AM UTC is 4 AM EST—not handling that properly creates chaos.
This lesson teaches you all three skills, emphasizing that you don't memorize all 30+ format codes. You understand the pattern and ask AI when needed.
Formatting Dates and Times with strftime()
Strings. That's how humans read dates. The strftime() method converts datetime objects into human-readable text using format codes.
Understanding Format Codes
Each format code starts with % and represents a specific part of the date:
from datetime import datetime, timezone
# Create a specific datetime (November 9, 2025 at 2:30:45 PM UTC)
moment: datetime = datetime(2025, 11, 9, 14, 30, 45, tzinfo=timezone.utc)
# Common format codes
print(moment.strftime("%Y-%m-%d")) # 2025-11-09 (year-month-day)
print(moment.strftime("%H:%M:%S")) # 14:30:45 (hours:minutes:seconds)
print(moment.strftime("%A, %B %d")) # Sunday, November 09 (weekday, full month, day)
The key insight: You don't memorize all 30+ codes. You learn the common ones (%Y, %m, %d, %H, %M, %S, %A, %B) and ask AI when you need something specific.
💬 AI Colearning Prompt
"Explain the difference between
%Yand%y, and between%Hand%I. When would you use each?"
Common Format Patterns
Here are the patterns you'll use most often:
from datetime import datetime, timezone
moment: datetime = datetime(2025, 11, 9, 14, 30, 45, tzinfo=timezone.utc)
# ISO 8601 (standard for data storage and APIs)
iso_format: str = moment.strftime("%Y-%m-%dT%H:%M:%S%z")
print(iso_format) # 2025-11-09T14:30:45+0000
# US Format (casual, for display)
us_format: str = moment.strftime("%m/%d/%Y %I:%M %p")
print(us_format) # 11/09/2025 02:30 PM
# European Format
eu_format: str = moment.strftime("%d/%m/%Y %H:%M")
print(eu_format) # 09/11/2025 14:30
# Friendly Format (for humans)
friendly: str = moment.strftime("%A, %B %d, %Y at %I:%M %p")
print(friendly) # Sunday, November 09, 2025 at 02:30 PM
Notice how the same moment looks different depending on the format. Your job isn't to remember these strings—it's to choose the right format for your audience.
🎓 Instructor Commentary
In AI-native development, you don't memorize all 30+ format codes. You understand the pattern (
%Y= year,%m= month,%d= day) and ask AI when you need a specific format. Syntax is cheap; knowing WHEN to use ISO 8601 vs localized format is gold. ISO for data storage and APIs. Friendly format for user interfaces.
Practical Formatting Exercise
Let's build a function that formats a datetime in multiple ways:
from datetime import datetime, timezone
def format_datetime_multiple_ways(dt: datetime) -> dict[str, str]:
"""
Format a datetime object in multiple contexts.
Args:
dt: A datetime object (timezone-aware preferred)
Returns:
Dictionary with formatted strings for different contexts
"""
return {
"iso": dt.strftime("%Y-%m-%dT%H:%M:%S"),
"us": dt.strftime("%m/%d/%Y"),
"friendly": dt.strftime("%A, %B %d, %Y"),
"time_only": dt.strftime("%I:%M %p"),
"timestamp": dt.strftime("%s"), # Unix timestamp as string
}
# Test it
now: datetime = datetime.now(timezone.utc)
formats = format_datetime_multiple_ways(now)
for context, formatted in formats.items():
print(f"{context}: {formatted}")
This function shows the real-world pattern: capture different formats once, use them throughout your application.
🚀 CoLearning Challenge
Ask your AI Co-Teacher:
"Generate code that formats a datetime in both ISO 8601 and 'Meeting on Saturday, 2:30 PM' format. Then explain what each format code does."
Expected Outcome: You'll understand how to combine format codes to create human-friendly output.
Working with Time Differences: Timedelta
A timedelta represents a duration—the difference between two points in time. Unlike datetime (which represents a specific moment), timedelta represents "how much time."
Creating Timedelta Objects
from datetime import timedelta
# Create durations
one_day: timedelta = timedelta(days=1)
three_hours: timedelta = timedelta(hours=3)
two_weeks: timedelta = timedelta(weeks=2)
# Combine multiple units
complex_duration: timedelta = timedelta(
days=5,
hours=3,
minutes=30,
seconds=45
)
print(complex_duration) # 5 days, 3:30:45
print(complex_duration.total_seconds()) # 460245.0
Timedelta objects know how to convert between units automatically.
Date Arithmetic: Adding and Subtracting
Now here's where timedelta shines. Add it to a datetime to get a future date:
from datetime import datetime, timedelta, timezone
# Start with today
today: datetime = datetime.now(timezone.utc)
# Calculate important dates
tomorrow: datetime = today + timedelta(days=1)
next_week: datetime = today + timedelta(weeks=1)
next_year: datetime = today + timedelta(days=365)
# Go backwards
yesterday: datetime = today - timedelta(days=1)
last_month: datetime = today - timedelta(days=30)
print(f"Today: {today.strftime('%Y-%m-%d')}")
print(f"Tomorrow: {tomorrow.strftime('%Y-%m-%d')}")
print(f"In 30 days: {next_month.strftime('%Y-%m-%d')}")
Timedelta handles all the complexity for you—leap years, different month lengths, everything.
✨ Teaching Tip
Use Claude Code to explore edge cases: "Add 30 days to January 15. Then check: does it land on Feb 14? Why not 45 days?" This teaches you timedelta respects calendar reality.
Calculating Duration Between Two Moments
One of the most practical uses: find how much time has passed or remains:
from datetime import datetime, timezone
# Two specific moments
launch_time: datetime = datetime(2025, 11, 9, 14, 30, tzinfo=timezone.utc)
current_time: datetime = datetime.now(timezone.utc)
# Calculate the difference
elapsed: timedelta = current_time - launch_time
print(f"Days elapsed: {elapsed.days}")
print(f"Total seconds: {elapsed.total_seconds()}")
print(f"Hours: {elapsed.total_seconds() / 3600:.1f}")
# Practical use: countdown
deadline: datetime = datetime(2025, 12, 31, 23, 59, tzinfo=timezone.utc)
remaining: timedelta = deadline - current_time
if remaining.total_seconds() > 0:
print(f"Time until deadline: {remaining.days} days, {remaining.seconds // 3600} hours")
else:
print("Deadline passed!")
Notice we calculate remaining and check if it's positive. This is the pattern for deadlines, event scheduling, and countdowns.
🎓 Instructor Commentary
You're not calculating duration by hand (that's error-prone and wasteful). You subtract two datetime objects and timedelta does the work. Syntax is cheap; knowing to subtract datetime objects and handle the result is gold. This is why we use timedelta.
Code Example: Duration Calculation with Validation
from datetime import datetime, timedelta, timezone
def time_until_event(event_time: datetime) -> str:
"""
Calculate human-readable time remaining until an event.
Args:
event_time: A future datetime object
Returns:
Formatted string like "2 days, 3 hours"
"""
now: datetime = datetime.now(timezone.utc)
remaining: timedelta = event_time - now
if remaining.total_seconds() <= 0:
return "Event already happened!"
days = remaining.days
hours = remaining.seconds // 3600
minutes = (remaining.seconds % 3600) // 60
# Build friendly string
parts: list[str] = []
if days > 0:
parts.append(f"{days} day{'s' if days != 1 else ''}")
if hours > 0:
parts.append(f"{hours} hour{'s' if hours != 1 else ''}")
if minutes > 0:
parts.append(f"{minutes} minute{'s' if minutes != 1 else ''}")
return ", ".join(parts) if parts else "Less than a minute"
# Test it
conference: datetime = datetime(2025, 12, 15, 9, 0, tzinfo=timezone.utc)
print(time_until_event(conference)) # Might print "36 days, 5 hours"
This function shows a real pattern: calculate timedelta, extract components, format for humans.
Converting Between Timezones
Timezones are your biggest challenge in datetime work. A time can't be "correct" or "incorrect" without a timezone—you need to know: "4 PM what? UTC? EST? JST?"
The Timezone Object
Python represents timezone offsets with the timezone class:
from datetime import datetime, timezone, timedelta
# UTC (the reference point)
utc_now: datetime = datetime.now(timezone.utc)
print(utc_now) # Shows timezone info: ...+00:00
# Other timezones are offsets from UTC
eastern: timezone = timezone(timedelta(hours=-5)) # EST (UTC-5)
pacific: timezone = timezone(timedelta(hours=-8)) # PST (UTC-8)
# Create a specific time in a timezone
meeting_time: datetime = datetime(2025, 11, 9, 14, 30, tzinfo=eastern)
print(meeting_time) # Shows: 2025-11-09 14:30:00-05:00
The :−5 (negative 5) means "5 hours behind UTC." When it's noon UTC, it's 7 AM Eastern.
💬 AI Colearning Prompt
"Explain why timezone offsets are negative for Western Hemisphere and positive for Eastern Hemisphere."
Converting UTC to Local Time
Here's the practical scenario: you have a UTC timestamp (from a database), and you need to show the user their local time.
from datetime import datetime, timezone, timedelta
# A meeting time in UTC
meeting_utc: datetime = datetime(2025, 11, 9, 14, 30, tzinfo=timezone.utc)
# Convert to different timezones
eastern: timezone = timezone(timedelta(hours=-5))
pacific: timezone = timezone(timedelta(hours=-8))
london: timezone = timezone(timedelta(hours=0)) # UTC
tokyo: timezone = timezone(timedelta(hours=9))
# Use astimezone() to convert
meeting_eastern = meeting_utc.astimezone(eastern)
meeting_pacific = meeting_utc.astimezone(pacific)
meeting_tokyo = meeting_utc.astimezone(tokyo)
print(f"Meeting UTC: {meeting_utc.strftime('%H:%M')}")
print(f"Meeting Eastern: {meeting_eastern.strftime('%H:%M')}") # 09:30
print(f"Meeting Pacific: {meeting_pacific.strftime('%H:%M')}") # 06:30
print(f"Meeting Tokyo: {meeting_tokyo.strftime('%H:%M')}") # 23:30
The magic: astimezone() adjusts both the time AND the offset automatically.
🚀 CoLearning Challenge
Ask your AI Co-Teacher:
"Generate a function that takes a UTC timestamp and returns a dictionary with the time in NYC, London, and Tokyo timezones. Then explain what astimezone() does under the hood."
Expected Outcome: You'll understand how to display times for a global audience.
Real-World Timezone Conversion: Meeting Scheduler
from datetime import datetime, timezone, timedelta
class MeetingScheduler:
"""Helper for scheduling meetings across timezones."""
def __init__(self, meeting_utc: datetime):
"""
Args:
meeting_utc: Meeting time in UTC (timezone-aware)
"""
if meeting_utc.tzinfo is None:
raise ValueError("Must provide timezone-aware datetime")
self.meeting_utc = meeting_utc.astimezone(timezone.utc)
def time_in(self, timezone_obj: timezone) -> str:
"""Show meeting time in a specific timezone."""
local = self.meeting_utc.astimezone(timezone_obj)
return local.strftime("%H:%M (%Z)")
def create_agenda(self) -> str:
"""Generate meeting agenda with multiple timezones."""
timezones = {
"NYC": timezone(timedelta(hours=-5)),
"London": timezone(timedelta(hours=0)),
"Tokyo": timezone(timedelta(hours=9)),
}
lines = ["MEETING AGENDA\n"]
for city, tz in timezones.items():
local = self.meeting_utc.astimezone(tz)
lines.append(f"{city}: {local.strftime('%A %I:%M %p')}")
return "\n".join(lines)
# Use it
meeting = MeetingScheduler(datetime(2025, 11, 9, 14, 30, tzinfo=timezone.utc))
print(meeting.create_agenda())
Output:
MEETING AGENDA
NYC: Sunday 09:30 AM
London: Sunday 02:30 PM
Tokyo: Monday 11:30 PM
This is real production code. You're solving an actual problem: helping people understand when a meeting happens in their timezone.
🎓 Instructor Commentary
You don't calculate timezone offsets in your head (that's why timezones exist as objects). You understand: "UTC is the reference, offsets are +/- hours, astimezone() does the conversion." Syntax is cheap; knowing to keep everything in UTC internally and convert on display is gold. This pattern prevents bugs.
Handling Timezone Edge Cases
Timezones aren't simple. During daylight saving time transitions, an hour doesn't exist (spring forward) or exists twice (fall back). For now, know this happens and use AI when you encounter it.
✨ Teaching Tip
When building a production scheduling system, ask your AI: "How do I handle times during DST transitions? When a time is ambiguous, which version should I pick?" This is where you lean on AI expertise—it's complex enough that asking beats guessing.
Summary of Formatting and Manipulation
Formatting with strftime(): Convert datetime objects to human-readable strings using format codes. You know the common ones (%Y, %m, %d, %H, %M, %S, %A, %B) and ask AI for others.
Manipulation with timedelta: Represent durations, add/subtract from datetimes, calculate differences. Always subtract datetime objects rather than calculating durations manually.
Conversion with timezone and astimezone(): Keep your system in UTC internally, convert to local timezones for display. Never hardcode timezone offsets—use timezone objects.
Try With AI
Use Claude Code or your AI companion tool to explore these concepts deeper. Work through the prompts below progressively, and don't be afraid to experiment with variations.
1. Recall: Common Format Codes
Ask your AI:
"List 5 common
strftimeformat codes (%Y, %m, %d, %H, %M) and show me what each produces for today's date."
Expected Output: A list of format codes with examples for the current date.
2. Understand: Timedelta vs Datetime
Ask your AI:
"Explain the difference between a
datetimeobject and atimedeltaobject. When would you use each?"
Expected Output: Clear explanation that datetime represents a moment, timedelta represents a duration, with practical examples.
3. Apply: Multi-Format Output Function
Ask your AI:
"Generate a function that takes any datetime and returns it formatted in three ways: ISO 8601, US format (MM/DD/YYYY), and friendly format ('Day, Month DD, YYYY'). Include type hints."
Expected Output: Working function with proper type hints that converts a datetime to three formats.
4. Analyze: Timezone Conversion Challenges
Ask your AI:
"What are the main challenges when converting times between timezones? How would you build a reliable system for a global application?"
Expected Output: Discussion of DST transitions, ambiguous times, and strategies to keep your system reliable.