Skip to main content

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 %Y and %y, and between %H and %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 strftime format 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 datetime object and a timedelta object. 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.