Date and Time Manipulation with datetime:

Date and Time Manipulation with datetime

Mastering Time Zones, Deltas, and Formatting

Photo by Eric O. IBEKWEM on Unsplash

Working with dates and times is a fundamental aspect of many software applications, from scheduling tasks to analyzing data. Python’s datetime module provides powerful tools for handling date and time calculations, but effectively managing time zones, time differences (deltas), and presentation (formatting) can be challenging. This article delves into these crucial areas, providing practical examples and explanations to solidify your understanding. We’ll go beyond the basics, covering best practices and common pitfalls to ensure you’re handling dates and times accurately and reliably in your Python projects.

1. Understanding the Basics of datetime

Before diving into the more advanced topics, let’s establish a solid foundation. The datetime module offers several key classes:

  • date: Represents a date (year, month, day).
  • time: Represents a time (hour, minute, second, microsecond).
  • datetime: Represents a combination of date and time.
  • timedelta: Represents a duration – the difference between two dates or times.
  • timezone: Represents a timezone. (We’ll focus on pytz and zoneinfo for concrete implementations.)

Let’s create some basic datetime objects:

import datetime

# Current date and time
now = datetime.datetime.now()
print(f"Current datetime (naive): {now}")

# Specific date and time
specific_datetime = datetime.datetime(2024, 1, 15, 10, 30, 0) # Year, Month, Day, Hour, Minute, Second
print(f"Specific datetime: {specific_datetime}")

# Today's date
today = datetime.date.today()
print(f"Today's date: {today}")

# A specific time
specific_time = datetime.time(14, 45, 30) # Hour, minute, Second
print(f"Specific time: {specific_time}")
  • datetime.datetime.now(): Creates a datetime object representing the current system date/time (naive). Example: Current datetime (naive): 2024-10-27 16:35:12.123456.
  • datetime.datetime(2024, 1, 15, 10, 30, 0): Creates a specific (naive) datetime. Output: Specific datetime: 2024-01-15 10:30:00.
  • datetime.date.today(): Returns a date object with today’s date. Example: Today’s date: 2024-10-27.
  • datetime.time(14, 45, 30): Creates a time object. Output: Specific time: 14:45:30.

2. Working with Time Zones

Handling time zones correctly is critical. Naive datetime objects (those without timezone information) can lead to errors.

2.1. Using pytz (The Traditional Approach – pre Python 3.9)

import datetime
import pytz

# Create a timezone object for US Eastern Time
eastern = pytz.timezone('US/Eastern')

# Create a naive datetime object
naive_datetime = datetime.datetime(2024, 1, 15, 10, 30, 0)

# Localize the naive datetime to US Eastern Time (make it aware)
localized_datetime = eastern.localize(naive_datetime)
print(f"Localized datetime (US/Eastern): {localized_datetime}")

# Get the current time in UTC
utc_now = datetime.datetime.now(pytz.utc)
print(f"Current UTC time: {utc_now}")

# Convert the UTC time to US Eastern Time
eastern_now = utc_now.astimezone(eastern)
print(f"Current US/Eastern time: {eastern_now}")

# Another example, with London
london_timezone = pytz.timezone('Europe/London')
london_time = utc_now.astimezone(london_timezone)
print(f"Current London time: {london_time}")
  • pytz.timezone(‘US/Eastern’): Creates a timezone object.
  • eastern.localize(naive_datetime): Makes the datetime timezone-aware. Example: Localized datetime (US/Eastern): 2024-01-15 10:30:00-05:00.
  • datetime.datetime.now(pytz.utc): Current time in UTC. Example: Current UTC time: 2024-10-27 20:35:12.123456+00:00.
  • utc_now.astimezone(eastern): Converts UTC time to Eastern Time.
  • The London example converts UTC to London time.

2.2. Using zoneinfo (The Preferred Approach – Python 3.9+)

import datetime
from zoneinfo import ZoneInfo

# Create a timezone object for US Eastern Time
eastern = ZoneInfo("America/New_York") # Use IANA names

# Create a naive datetime object
naive_datetime = datetime.datetime(2024, 1, 15, 10, 30, 0)

# Make the datetime timezone-aware (using replace)
aware_datetime = naive_datetime.replace(tzinfo=eastern)
print(f"Aware datetime (US/Eastern): {aware_datetime}")

# Get the current time in UTC
utc_now = datetime.datetime.now(ZoneInfo("UTC"))
print(f"Current UTC time: {utc_now}")

# Convert the UTC time to US Eastern Time
eastern_now = utc_now.astimezone(eastern)
print(f"Current US/Eastern time: {eastern_now}")
  • ZoneInfo(“America/New_York”): Creates a timezone object.
  • naive_datetime.replace(tzinfo=eastern): Makes the datetime timezone-aware. Example: Aware datetime (US/Eastern): 2024-01-15 10:30:00-05:00.
  • datetime.datetime.now(ZoneInfo(“UTC”)): Current time in UTC.
  • utc_now.astimezone(eastern): Converts to Eastern Time.

Key Difference: localize vs. replace

  • pytz.localize(): Handles ambiguous times during DST transitions. Can raise exceptions.
  • datetime.replace(tzinfo=…): Simply sets the tzinfo attribute. No DST handling.
  • With zoneinfo you can use datetime.astimezone() without previous using .replace(tzinfo=…).

Recommendation: Use zoneinfo for new projects (Python 3.9+).

3. Working with Time Deltas (timedelta)

import datetime

# Create two datetime objects
datetime1 = datetime.datetime(2024, 1, 15, 10, 0, 0)
datetime2 = datetime.datetime(2024, 1, 20, 15, 30, 0)

# Calculate the difference
delta = datetime2 - datetime1
print(f"Time difference: {delta}")
print(f"Total seconds: {delta.total_seconds()}")

# Add 5 days to a datetime
new_datetime = datetime1 + datetime.timedelta(days=5)
print(f"New datetime (after adding 5 days): {new_datetime}")

# Subtract 2 hours and 30 minutes
another_datetime = datetime2 - datetime.timedelta(hours=2, minutes=30)
print(f"New datetime (after subtracting 2h30m): {another_datetime}")

# Calculate a date 3 weeks from now
future_date = datetime.date.today() + datetime.timedelta(weeks=3)
print(f"{future_date}")
  • datetime2 – datetime1: Calculates the time difference. Output: Time difference: 5 days, 5:30:00.
  • delta.total_seconds(): Duration in seconds. Output: 451800.0.
  • datetime1 + datetime.timedelta(days=5): Adds 5 days. Output: New datetime (after adding 5 days): 2024-01-20 10:00:00.
  • datetime2 – datetime.timedelta(hours=2, minutes=30): Subtracts time. Output: New datetime (after subtracting 2h30m): 2024-01-20 13:00:00.
  • datetime.date.today() + datetime.timedelta(weeks=3): Adds three weeks to the current date.

4. Formatting Dates and Times (strftime and strptime)

  • strftime(): Converts a datetime object to a string.
  • strptime(): Converts a string to a datetime object.
import datetime

now = datetime.datetime.now()
# Format the datetime object into various strings
formatted_date1 = now.strftime("%Y-%m-%d %H:%M:%S") # ISO 8601 format
print(f"ISO 8601: {formatted_date1}")
formatted_date2 = now.strftime("%m/%d/%Y, %I:%M %p") # US date format
print(f"US Format: {formatted_date2}")
formatted_date3 = now.strftime("%A, %B %d, %Y") # Full weekday, month, day, year
print(f"Full Date: {formatted_date3}")

# Parse a string into a datetime object
date_string = "2024-03-10 18:45:00"
parsed_datetime = datetime.datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S")
print(f"Parsed datetime: {parsed_datetime}")

#Parse a string with only time
time_string = "12:30:50"
parsed_time = datetime.datetime.strptime(time_string, "%H:%M:%S").time()
print(f"Parsed time: {parsed_time}")

#Handle error
date_string_error = "2024-03-10"
try:
parsed_datetime_error = datetime.datetime.strptime(date_string_error, "%Y-%m-%d %H:%M:%S")
print(f"Parsed datetime error: {parsed_datetime_error}")
except ValueError as e:
print(f"Error: {e}")
  • now.strftime(“%Y-%m-%d %H:%M:%S”): ISO 8601 format. Example: ISO 8601: 2024-10-27 16:35:12.
  • now.strftime(“%m/%d/%Y, %I:%M %p”): US format. Example: US Format: 10/27/2024, 04:35 PM.
  • now.strftime(“%A, %B %d, %Y”): Full date. Example: Full Date: Sunday, October 27, 2024.
  • datetime.datetime.strptime(date_string, “%Y-%m-%d %H:%M:%S”): Parses a string. Output: Parsed datetime: 2024-03-10 18:45:00.
  • datetime.datetime.strptime(time_string, “%H:%M:%S”).time(): This parses only the time portion of a string. Output: Parsed time: 12:30:50.
  • Error Handling: The try…except block demonstrates how to handle potential ValueError exceptions.

Format Codes (Commonly Used):

  Code   Description                       Example                              
------ --------------------------------- ----------------
%Y Year with century (4 digits) 2023
%y Year without century (2 digits) 23
%m Month (01-12) 01, 12
%d Day of the month (01-31) 01, 31
%H Hour (24-hour clock, 00-23) 00, 23
%I Hour (12-hour clock, 01-12) 01, 12
%M Minute (00-59) 00, 59
%S Second (00-59) 00, 59
%f Microsecond (000000-999999) 000000, 999999

Important Considerations:

  • Locale: strftime() output (e.g., weekday/month names) depends on the locale.
  • strptime and Timezones: strptime doesn’t handle timezones by itself. Combine with pytz or zoneinfo.
  • ISO 8601: Use %Y-%m-%dT%H:%M:%S%z for timezone-aware string representation.

Parsing a String with Timezone and Creating an Aware Datetime

import datetime
from zoneinfo import ZoneInfo

date_string_with_tz = "2024-07-15T12:30:00-0400"

# 1. Parse the string using strptime (creates a naive datetime)
parsed_datetime = datetime.datetime.strptime(date_string_with_tz, "%Y-%m-%dT%H:%M:%S%z")
print(f"Parsed (naive): {parsed_datetime}")

# 2. Use astimezone to properly handle the offset, no need to replace.
aware_datetime = parsed_datetime.astimezone(ZoneInfo("UTC"))
print(f"UTC timezone-aware: {aware_datetime}")
aware_datetime_et = parsed_datetime.astimezone(ZoneInfo("America/New_York"))
print(f"EST timezone-aware: {aware_datetime_et}")
  • Parsing: strptime with %z handles the offset, but the result is naive: Parsed (naive): 2024-07-15 12:30:00-04:00.
  • Convert to UTC: We used astimezone Output: UTC timezone-aware: 2024-07-15 16:30:00+00:00.
  • Convert to EST: We used astimezone Output: EST timezone-aware: 2024-07-15 12:30:00-04:00.

The datetime module is essential for handling dates and times in Python. Understanding time zones (pytz or zoneinfo), time deltas (timedelta), and formatting (strftime, strptime) is crucial for building robust applications. Choose zoneinfo for new projects, distinguish between naive and aware datetime objects, and handle parsing errors. Mastering these concepts will improve the reliability of your code.


Date and Time Manipulation with datetime: was originally published in ScriptSerpent on Medium, where people are continuing the conversation by highlighting and responding to this story.

Scroll to Top