Home Tutorials Standard Library

Standard Library

datetime in Python: Dates, Times, Arithmetic, and Timezones

Pyford Notes July 1, 2026 7 min read
Key points
  • date holds year/month/day; datetime adds hour, minute, second, microsecond.
  • Subtract two datetime objects to get a timedelta; add a timedelta to shift a date.
  • strftime(format) converts to a string; strptime(string, format) parses back to a datetime.
  • Prefer datetime.now(tz=timezone.utc) over naive datetimes when working across timezones.

date, time, and datetime

The datetime module exports three main classes for representing points in time:

  • date — stores year, month, and day. No time-of-day information.
  • time — stores hour, minute, second, and microsecond. No calendar date.
  • datetime — combines both: year, month, day, hour, minute, second, microsecond, and optional timezone info (tzinfo).

A fourth class, timedelta, represents a duration — the result of subtracting two dates or datetimes, or a duration you construct to add or subtract from a date.

All four are imported from the same module:

from datetime import date, time, datetime, timedelta, timezone

Creating instances

Follow these steps to create datetime objects:

  1. Specify values directly. Pass year, month, and day (and optionally time components) as positional arguments:
    d = date(2026, 7, 1)          # July 1, 2026
    t = time(14, 30, 0)           # 14:30:00
    dt = datetime(2026, 7, 1, 14, 30, 0)
    
  2. Use class methods for common values. date.today() and datetime.now() give the current date/time:
    today = date.today()
    now   = datetime.now()
    
  3. Build from a Unix timestamp. datetime.fromtimestamp(ts) converts a POSIX timestamp to local time; datetime.utcfromtimestamp(ts) gives UTC (but returns a naive object — prefer datetime.fromtimestamp(ts, tz=timezone.utc) instead):
    import time as time_module
    ts = time_module.time()
    dt_utc = datetime.fromtimestamp(ts, tz=timezone.utc)
    
  4. Combine a date and a time object.
    d = date(2026, 7, 1)
    t = time(9, 0)
    dt = datetime.combine(d, t)
    

Date and time arithmetic with timedelta

Subtract two date or datetime objects to get a timedelta. Add or subtract a timedelta to shift a date:

from datetime import date, timedelta

start = date(2026, 1, 1)
end   = date(2026, 7, 1)

gap = end - start
print(gap.days)    # 181

two_weeks = timedelta(weeks=2)
deadline = end + two_weeks
print(deadline)    # 2026-07-15

timedelta stores its value internally as days and seconds. Access them via .days, .seconds, and .microseconds. For the total duration in seconds, use .total_seconds():

d = timedelta(hours=36, minutes=30)
print(d.total_seconds())   # 131400.0

Formatting with strftime()

Call .strftime(format_string) on any date, time, or datetime object to produce a formatted string:

from datetime import datetime

dt = datetime(2026, 7, 1, 14, 30, 5)
print(dt.strftime("%Y-%m-%d"))          # 2026-07-01
print(dt.strftime("%d %B %Y"))          # 01 July 2026
print(dt.strftime("%I:%M %p"))          # 02:30 PM
print(dt.strftime("%A, %d %b %Y"))      # Wednesday, 01 Jul 2026
Common strftime format codes
CodeMeaningExample
%Y4-digit year2026
%mZero-padded month (01–12)07
%dZero-padded day (01–31)01
%BFull month nameJuly
%bAbbreviated month nameJul
%AFull weekday nameWednesday
%HHour in 24 h format (00–23)14
%IHour in 12 h format (01–12)02
%MMinutes (00–59)30
%SSeconds (00–59)05
%pAM or PMPM
%ZTimezone nameUTC

Parsing strings with strptime()

datetime.strptime(string, format) is the inverse of strftime. It parses a string into a datetime object using the same format codes:

from datetime import datetime

s = "01 July 2026 14:30"
dt = datetime.strptime(s, "%d %B %Y %H:%M")
print(dt.year, dt.month, dt.day)   # 2026 7 1

If the format does not match, Python raises ValueError. Wrap in a try/except block when parsing user-supplied strings:

try:
    dt = datetime.strptime(user_input, "%Y-%m-%d")
except ValueError:
    print("Invalid date format. Expected YYYY-MM-DD.")

Timezone-aware datetimes

Important: A naive datetime has no timezone attached. A aware datetime carries a tzinfo object. Mixing naive and aware datetimes in arithmetic raises TypeError. Always pick one convention and stick with it throughout your application.

The simplest way to work with UTC is to use the built-in timezone.utc constant:

from datetime import datetime, timezone

now_utc = datetime.now(tz=timezone.utc)
print(now_utc)
# 2026-07-01 14:30:05.123456+00:00

To work with other timezones, install the zoneinfo module (part of the standard library since Python 3.9) or the third-party pytz library:

from datetime import datetime
from zoneinfo import ZoneInfo

london = ZoneInfo("Europe/London")
tokyo  = ZoneInfo("Asia/Tokyo")

now_london = datetime.now(tz=london)
now_tokyo  = now_london.astimezone(tokyo)
print(now_tokyo)   # same instant, different offset

date.today() vs datetime.now()

date.today() returns a date object (year, month, day only) using local time. datetime.now() returns a naive datetime in local time. datetime.now(tz=timezone.utc) returns an aware datetime in UTC.

For logging, API timestamps, and any situation where two machines in different timezones might compare values, always use the UTC-aware form. For simple date arithmetic where only calendar dates matter (age calculation, day-of-week lookup), date.today() is perfectly appropriate.

from datetime import date, datetime, timezone

print(date.today())                     # 2026-07-01
print(datetime.now())                   # 2026-07-01 14:30:05.123456  (naive)
print(datetime.now(tz=timezone.utc))    # 2026-07-01 14:30:05.123456+00:00  (aware)