Python Tutorial
Working with Dates and Times in Python
Dates and times appear in nearly every application: order timestamps, due dates, age calculations, subscription renewals, scheduling across time zones. Python's datetime module provides date, time, datetime and timedelta types, zoneinfo provides real time zones, and calendar and time cover the rest.
This lesson covers creating dates, formatting and parsing with strftime/strptime and ISO 8601, date arithmetic, time zones done correctly, and common business calculations.
The Core Types
date(2026, 9, 27) is a calendar date; time(14, 30) a time of day; datetime(2026, 9, 27, 14, 30) both; timedelta(days=7, hours=3) a duration. date.today() and datetime.now() give the current values. Objects are immutable — replace() returns a modified copy.
Formatting and Parsing
strftime(format) turns a datetime into text and datetime.strptime(text, format) parses text. Common codes: %Y year, %m month, %d day, %H/%I hour (24/12), %M minute, %S second, %p AM/PM, %A/%a weekday name, %B/%b month name. For data exchange use ISO 8601: isoformat() and fromisoformat().
Arithmetic
Adding a timedelta moves a date; subtracting two dates gives a timedelta (.days, .total_seconds()). Dates compare with < and >. Months are not fixed-length, so "add one month" needs care — the third-party python-dateutil library provides relativedelta for that.
Time Zones
A naive datetime has no time zone; an aware one does. Store and compute in UTC (datetime.now(timezone.utc)) and convert to local time only for display, using zoneinfo.ZoneInfo("Asia/Kolkata") and astimezone(). ZoneInfo handles daylight-saving rules correctly. Never compare naive and aware datetimes.
Examples
Creating dates, times, datetimes and durations
from datetime import date, time, datetime, timedelta
d = date(2026, 9, 27)
t = time(14, 30, 15)
dt = datetime(2026, 9, 27, 14, 30)
print(d, t, dt)
print(d.year, d.month, d.day, d.weekday(), d.isoweekday(), d.strftime("%A"))
print(dt.date(), dt.time(), datetime.combine(d, t))
print(d.replace(year=2027), timedelta(days=1, hours=3).total_seconds())
print(type(date.today()).__name__, type(datetime.now()).__name__)
2026-09-27 14:30:15 2026-09-27 14:30:00
2026 9 27 6 7 Sunday
2026-09-27 14:30:00 2026-09-27 14:30:15
2027-09-27 97200.0
date datetime
Formatting with strftime and parsing with strptime and ISO 8601
from datetime import datetime
dt = datetime(2026, 9, 27, 18, 5, 9)
print(dt.strftime("%d/%m/%Y %H:%M"))
print(dt.strftime("%a, %d %b %Y %I:%M %p"))
print(dt.strftime("%B %d, %Y"), dt.strftime("%Y%m%d"))
parsed = datetime.strptime("27-09-2026 06:30 PM", "%d-%m-%Y %I:%M %p")
print(parsed, parsed.hour)
iso = dt.isoformat()
print(iso, datetime.fromisoformat(iso) == dt)
try:
datetime.strptime("2026/09/27", "%d-%m-%Y")
except ValueError as e:
print("ValueError:", e)
27/09/2026 18:05
Sun, 27 Sep 2026 06:05 PM
September 27, 2026 20260927
2026-09-27 18:30:00 18
2026-09-27T18:05:09 True
ValueError: time data '2026/09/27' does not match format '%d-%m-%Y'
Date arithmetic: due dates, ages and countdowns
from datetime import date, datetime, timedelta
order_day = date(2026, 9, 27)
print("delivery by", order_day + timedelta(days=5))
print("return window closes", order_day + timedelta(weeks=2))
def age(born, today):
return today.year - born.year - ((today.month, today.day) < (born.month, born.day))
print("age:", age(date(2001, 12, 15), date(2026, 9, 27)))
start = datetime(2026, 9, 27, 9, 15)
end = datetime(2026, 9, 28, 11, 45)
gap = end - start
print(gap, gap.days, gap.seconds // 3600, gap.total_seconds() / 3600)
print(date(2026, 10, 1) > order_day, (date(2026, 12, 31) - order_day).days, "days left in 2026")
d = date(2026, 9, 27)
while d.weekday() >= 5: # move weekend dates to Monday
d += timedelta(days=1)
print("next working day:", d)
delivery by 2026-10-02
return window closes 2026-10-11
age: 24
1 day, 2:30:00 1 2 26.5
True 95 days left in 2026
next working day: 2026-09-28
Time zones with zoneinfo
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
utc_time = datetime(2026, 9, 27, 12, 0, tzinfo=timezone.utc)
for city, zone in [("Pune", "Asia/Kolkata"), ("London", "Europe/London"), ("New York", "America/New_York")]:
local = utc_time.astimezone(ZoneInfo(zone))
print(f"{city:<9} {local:%Y-%m-%d %H:%M %Z (UTC%z)}")
meeting = datetime(2026, 12, 1, 10, 0, tzinfo=ZoneInfo("Europe/London"))
print("London 10:00 in India:", meeting.astimezone(ZoneInfo("Asia/Kolkata")).strftime("%H:%M"))
naive = datetime(2026, 9, 27, 12, 0)
try:
naive < utc_time
except TypeError as e:
print("TypeError:", e)
Pune 2026-09-27 17:30 IST (UTC+0530)
London 2026-09-27 13:00 BST (UTC+0100)
New York 2026-09-27 08:00 EDT (UTC-0400)
London 10:00 in India: 15:30
TypeError: can't compare offset-naive and offset-aware datetimes
The calendar and time modules
import calendar
import time
print(calendar.month(2026, 9))
first_weekday, days = calendar.monthrange(2026, 2) # weekday 0=Mon ... 6=Sun
print(calendar.isleap(2028), (int(first_weekday), days), calendar.day_name[0])
start = time.perf_counter()
time.sleep(0.1)
print("slept about", round(time.perf_counter() - start, 1), "seconds")
print(time.strftime("%Y", time.gmtime(0)))
September 2026
Mo Tu We Th Fr Sa Su
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
True (6, 28) Monday
slept about 0.1 seconds
1970
Common Mistakes
- Storing local times without a time zone, causing bugs around daylight saving and in multi-region apps.
- Comparing naive and aware datetimes (TypeError).
- Mixing up %m (month) and %M (minute) in format strings.
- Adding 30 days to mean "one month".
- Using datetime.utcnow() (deprecated) instead of datetime.now(timezone.utc).
Key Points to Remember
- date, time, datetime and timedelta are the core types.
- strftime formats, strptime parses; use ISO 8601 for data exchange.
- Subtracting dates gives timedeltas; add timedeltas to move dates.
- Store UTC, convert with zoneinfo.ZoneInfo for display.
- calendar and time cover calendars, sleeping and precise timing.
Practice the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.