Skip to main content

Dates and Times

What is the datetime module?

The datetime module provides classes for working with dates and times in Python. It's part of the standard library and gives you everything you need to create, manipulate, format, and calculate with dates and times.

from datetime import datetime, date, time

now = datetime.now()
print(now) # 2024-01-15 14:30:45.123456

today = date.today()
print(today) # 2024-01-15

Why this matters

Dates and times are everywhere in programming from logging events, scheduling tasks, calculating durations, parsing user input, working with databases, and more. The datetime module gives you:

  • Precise date/time handling: Work with dates, times, and combinations of both
  • Timezone support: Handle different timezones (with timezone and timedelta)
  • Formatting and parsing: Convert between strings and datetime objects
  • Date arithmetic: Calculate differences, add/subtract time periods
  • Calendar operations: Get weekdays, determine if dates fall on weekends, etc.

Without proper date/time handling, you'd struggle with timezone conversions, leap years, daylight saving time, and other complexities. The datetime module handles all of this for you.

How dates and times are stored

Understanding how computers store dates and times helps you work with the datetime module more effectively.

Unix epoch time

Computers typically store dates and times as a number representing seconds (or milliseconds) since a fixed point in time called the epoch. The Unix epoch is January 1, 1970, 00:00:00 UTC.

import time
from datetime import datetime

# Get current time as seconds since epoch
epoch_seconds = time.time()
print(epoch_seconds) # 1705327845.123456 (example)

# Convert epoch time to datetime
dt = datetime.fromtimestamp(epoch_seconds)
print(dt) # 2024-01-15 14:30:45.123456

# Convert datetime back to epoch time
epoch_seconds = dt.timestamp()
print(epoch_seconds) # 1705327845.123456

Why epoch time?

Storing dates as numbers since the epoch makes it easy to:

  • Calculate differences: Subtract two epoch times to get the duration
  • Compare dates: Compare numbers directly
  • Store efficiently: A single number instead of multiple fields
  • Handle timezones: Convert to/from UTC consistently

Databases and epoch time

Most databases store dates and times as epoch seconds or milliseconds. This is why you'll often need to convert between datetime objects and timestamps when working with databases:

from datetime import datetime

# When saving to database: convert datetime to timestamp
dt = datetime.now()
db_timestamp = int(dt.timestamp()) # Store as integer seconds
# or
db_timestamp_ms = int(dt.timestamp() * 1000) # Store as milliseconds

# When reading from database: convert timestamp to datetime
stored_timestamp = 1705327845
dt = datetime.fromtimestamp(stored_timestamp)
print(dt) # 2024-01-15 14:30:45

Different databases use different formats:

  • PostgreSQL, MySQL: Often store as TIMESTAMP (internally epoch seconds)
  • SQLite: Stores as text, integer (epoch seconds), or real (Julian day)
  • MongoDB: Uses BSON Date type (milliseconds since epoch)
  • Redis: Stores as integer (seconds) or string

The datetime module's timestamp() and fromtimestamp() methods make it easy to convert between Python's datetime objects and the numeric formats databases use.

Internal representation

Python's datetime objects store dates and times internally as separate components (year, month, day, hour, minute, second, microsecond), but they can easily convert to and from epoch time when needed:

from datetime import datetime

# Create a datetime
dt = datetime(2024, 1, 15, 14, 30, 45)

# Convert to timestamp (seconds since epoch)
timestamp = dt.timestamp()
print(timestamp) # 1705327845.0

# Create from timestamp
dt2 = datetime.fromtimestamp(timestamp)
print(dt2) # 2024-01-15 14:30:45

The datetime module abstracts away the epoch time details, letting you work with human readable dates and times while handling the underlying representation automatically.

The main classes

The datetime module provides several classes:

  • date - Represents a date (year, month, day)
  • time - Represents a time (hour, minute, second, microsecond)
  • datetime - Represents both date and time
  • timedelta - Represents a duration (difference between dates/times)
  • timezone - Represents timezone information

Creating datetime objects

Current date and time

from datetime import datetime, date, time

# Current date and time
now = datetime.now()
print(now) # 2024-01-15 14:30:45.123456

# Current date only
today = date.today()
print(today) # 2024-01-15

# Current time only (requires a date reference)
current_time = datetime.now().time()
print(current_time) # 14:30:45.123456

Creating specific dates and times

from datetime import datetime, date, time

# Create a specific date
birthday = date(1990, 5, 15)
print(birthday) # 1990-05-15

# Create a specific time
meeting_time = time(14, 30, 0) # 2:30 PM
print(meeting_time) # 14:30:00

# Create a specific datetime
event = datetime(2024, 12, 25, 10, 0, 0)
print(event) # 2024-12-25 10:00:00

# Or combine date and time
event_date = date(2024, 12, 25)
event_time = time(10, 0, 0)
event = datetime.combine(event_date, event_time)
print(event) # 2024-12-25 10:00:00

Parsing strings

from datetime import datetime

# Parse a string into a datetime object
date_string = "2024-01-15 14:30:00"
dt = datetime.strptime(date_string, "%Y-%m-%d %H:%M:%S")
print(dt) # 2024-01-15 14:30:00

# Common format strings
dt1 = datetime.strptime("2024-01-15", "%Y-%m-%d")
dt2 = datetime.strptime("01/15/2024", "%m/%d/%Y")
dt3 = datetime.strptime("January 15, 2024", "%B %d, %Y")

Accessing date and time components

You can access individual components of datetime objects:

from datetime import datetime

now = datetime.now()

print(now.year) # 2024
print(now.month) # 1
print(now.day) # 15
print(now.hour) # 14
print(now.minute) # 30
print(now.second) # 45
print(now.microsecond) # 123456

# Day of the week (Monday is 0, Sunday is 6)
print(now.weekday()) # 0 (Monday)
print(now.isoweekday()) # 1 (Monday is 1, Sunday is 7)

Formatting dates and times

Convert datetime objects to strings using strftime():

from datetime import datetime

now = datetime.now()

# Common format codes
print(now.strftime("%Y-%m-%d")) # 2024-01-15
print(now.strftime("%B %d, %Y")) # January 15, 2024
print(now.strftime("%m/%d/%Y")) # 01/15/2024
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")) # Monday, January 15, 2024
print(now.strftime("%Y-%m-%d %H:%M:%S")) # 2024-01-15 14:30:45

Common format codes

CodeMeaningExample
%YYear with century2024
%yYear without century24
%mMonth as number01
%BFull month nameJanuary
%bAbbreviated monthJan
%dDay of month15
%AFull weekday nameMonday
%aAbbreviated weekdayMon
%HHour (24-hour)14
%IHour (12-hour)02
%MMinute30
%SSecond45
%pAM/PMPM

Date and time arithmetic

Using timedelta

timedelta represents a duration, which is the difference between two dates or times:

from datetime import datetime, timedelta

now = datetime.now()

# Add time
tomorrow = now + timedelta(days=1)
next_week = now + timedelta(weeks=1)
in_2_hours = now + timedelta(hours=2)
in_30_minutes = now + timedelta(minutes=30)

# Subtract time
yesterday = now - timedelta(days=1)
last_week = now - timedelta(weeks=1)

# Combine units
future = now + timedelta(days=5, hours=3, minutes=30)

Calculating differences

from datetime import datetime, timedelta

date1 = datetime(2024, 1, 15)
date2 = datetime(2024, 1, 20)

difference = date2 - date1
print(difference) # 5 days, 0:00:00
print(difference.days) # 5
print(difference.seconds) # 0
print(difference.total_seconds()) # 432000.0

Comparing dates

from datetime import datetime

date1 = datetime(2024, 1, 15)
date2 = datetime(2024, 1, 20)

print(date1 < date2) # True
print(date1 > date2) # False
print(date1 == date2) # False
print(date1 != date2) # True

Working with timezones

Naive vs aware datetime objects

  • Naive: No timezone information (most common)
  • Aware: Has timezone information
from datetime import datetime, timezone, timedelta

# Naive datetime (no timezone)
naive = datetime.now()
print(naive.tzinfo) # None

# Aware datetime (with timezone)
utc_now = datetime.now(timezone.utc)
print(utc_now.tzinfo) # UTC

# Create timezone-aware datetime
eastern = timezone(timedelta(hours=-5)) # EST
dt_eastern = datetime.now(eastern)
print(dt_eastern) # 2024-01-15 09:30:45.123456-05:00

Converting between timezones

from datetime import datetime, timezone, timedelta

# Create timezone objects
utc = timezone.utc
eastern = timezone(timedelta(hours=-5))
pacific = timezone(timedelta(hours=-8))

# Start with UTC time
utc_time = datetime(2024, 1, 15, 14, 30, 0, tzinfo=utc)

# Convert to Eastern
eastern_time = utc_time.astimezone(eastern)
print(eastern_time) # 2024-01-15 09:30:00-05:00

# Convert to Pacific
pacific_time = utc_time.astimezone(pacific)
print(pacific_time) # 2024-01-15 06:30:00-08:00

Common patterns

Get start/end of day

from datetime import datetime, time

now = datetime.now()

# Start of day (midnight)
start_of_day = datetime.combine(now.date(), time.min)
print(start_of_day) # 2024-01-15 00:00:00

# End of day (just before midnight)
end_of_day = datetime.combine(now.date(), time.max)
print(end_of_day) # 2024-01-15 23:59:59.999999

Calculate age

from datetime import date

def calculate_age(birth_date):
today = date.today()
age = today.year - birth_date.year
# Adjust if birthday hasn't occurred this year
if (today.month, today.day) < (birth_date.month, birth_date.day):
age -= 1
return age

birthday = date(1990, 5, 15)
print(calculate_age(birthday)) # 33 (example)

Find next occurrence of a weekday

from datetime import datetime, timedelta

def next_weekday(start_date, weekday):
"""Find next occurrence of weekday (0=Monday, 6=Sunday)"""
days_ahead = weekday - start_date.weekday()
if days_ahead <= 0: # Target day already happened this week
days_ahead += 7
return start_date + timedelta(days=days_ahead)

today = datetime.now().date()
next_monday = next_weekday(today, 0) # 0 = Monday
print(next_monday)

Format relative time

from datetime import datetime, timedelta

def time_ago(dt):
"""Return human-readable time difference"""
now = datetime.now()
diff = now - dt

if diff.days > 365:
years = diff.days // 365
return f"{years} year{'s' if years > 1 else ''} ago"
elif diff.days > 30:
months = diff.days // 30
return f"{months} month{'s' if months > 1 else ''} ago"
elif diff.days > 0:
return f"{diff.days} day{'s' if diff.days > 1 else ''} ago"
elif diff.seconds > 3600:
hours = diff.seconds // 3600
return f"{hours} hour{'s' if hours > 1 else ''} ago"
elif diff.seconds > 60:
minutes = diff.seconds // 60
return f"{minutes} minute{'s' if minutes > 1 else ''} ago"
else:
return "just now"

past_time = datetime.now() - timedelta(hours=2)
print(time_ago(past_time)) # 2 hours ago

Best practices

  1. Use datetime for most cases: Unless you only need a date or time, datetime is usually the most flexible choice.
  2. Be aware of timezone issues: If your application deals with multiple timezones, always use timezone-aware datetime objects. Store times in UTC and convert to local timezones for display.
  3. Use strftime and strptime consistently: Stick to a consistent date format throughout your application to avoid parsing errors.
  4. Handle edge cases: Be careful with date arithmetic around month boundaries, leap years, and daylight saving time transitions.
  5. Use date.today() instead of datetime.now().date(): When you only need the date, date.today() is clearer and slightly more efficient.
  6. Prefer timedelta for date arithmetic: It handles all the complexities of months, years, and leap years correctly.
  7. Validate user input: When parsing date strings from users, always use try/except to handle invalid formats gracefully.
from datetime import datetime

def safe_parse_date(date_string):
"""Safely parse a date string, returning None if invalid"""
try:
return datetime.strptime(date_string, "%Y-%m-%d")
except ValueError:
return None

user_input = "2024-01-15"
date = safe_parse_date(user_input)
if date:
print(f"Valid date: {date}")
else:
print("Invalid date format")

The datetime module is essential for any Python program that deals with dates and times. Master it, and you'll be able to handle all kinds of temporal data with confidence!