Python Date & Time — datetime, time, calendar
Python ships three complementary modules for working with dates and times:
datetimedatetime— high-level objects for dates, times, and durations. This is what you reach for most.timetime— lower-level, OS-facing functions (timestamps, sleeping, performance timers).calendarcalendar— calendar-related helpers (leap years, weekday names, month grids).
import datetime, time, calendar
print(datetime.date.today()) # 2025-01-31
print(time.time()) # 1738291200.123 (seconds since epoch)
print(calendar.isleap(2024)) # Trueimport datetime, time, calendar
print(datetime.date.today()) # 2025-01-31
print(time.time()) # 1738291200.123 (seconds since epoch)
print(calendar.isleap(2024)) # TrueThe datetimedatetime module
It defines four key classes:
| Class | Represents |
|---|---|
datedate | A calendar date — year, month, day. |
timetime | A time of day — hour, minute, second, microsecond. |
datetimedatetime | A date and a time combined. |
timedeltatimedelta | A duration — the difference between two dates/times. |
Creating dates and times
from datetime import date, time, datetime
d = date(2025, 1, 31)
print(d.year, d.month, d.day) # 2025 1 31
t = time(14, 30, 15)
print(t.hour, t.minute) # 14 30
dt = datetime(2025, 1, 31, 14, 30, 15)
print(dt) # 2025-01-31 14:30:15
# "Now" helpers
print(date.today()) # today's date
print(datetime.now()) # local date + time right nowfrom datetime import date, time, datetime
d = date(2025, 1, 31)
print(d.year, d.month, d.day) # 2025 1 31
t = time(14, 30, 15)
print(t.hour, t.minute) # 14 30
dt = datetime(2025, 1, 31, 14, 30, 15)
print(dt) # 2025-01-31 14:30:15
# "Now" helpers
print(date.today()) # today's date
print(datetime.now()) # local date + time right nowAccessing parts
from datetime import datetime
dt = datetime(2025, 1, 31, 14, 30, 15)
print(dt.date()) # 2025-01-31
print(dt.time()) # 14:30:15
print(dt.weekday()) # 4 (Mon=0 ... Sun=6)
print(dt.isoweekday()) # 5 (Mon=1 ... Sun=7)
print(dt.isoformat()) # 2025-01-31T14:30:15from datetime import datetime
dt = datetime(2025, 1, 31, 14, 30, 15)
print(dt.date()) # 2025-01-31
print(dt.time()) # 14:30:15
print(dt.weekday()) # 4 (Mon=0 ... Sun=6)
print(dt.isoweekday()) # 5 (Mon=1 ... Sun=7)
print(dt.isoformat()) # 2025-01-31T14:30:15Formatting with strftimestrftime
strftimestrftime (“string format time”) turns a date/time into a string using format codes.
| Code | Meaning | Example |
|---|---|---|
%Y%Y | 4-digit year | 2025 |
%y%y | 2-digit year | 25 |
%m%m | Month, zero-padded | 01 |
%B%B | Full month name | January |
%b%b | Abbreviated month | Jan |
%d%d | Day of month | 31 |
%A%A | Full weekday name | Friday |
%a%a | Abbreviated weekday | Fri |
%H%H | Hour (24-hour) | 14 |
%I%I | Hour (12-hour) | 02 |
%M%M | Minute | 30 |
%S%S | Second | 15 |
%p%p | AM / PM | PM |
%j%j | Day of the year | 031 |
from datetime import datetime
dt = datetime(2025, 1, 31, 14, 30, 15)
print(dt.strftime("%Y-%m-%d")) # 2025-01-31
print(dt.strftime("%A, %B %d, %Y")) # Friday, January 31, 2025
print(dt.strftime("%I:%M %p")) # 02:30 PMfrom datetime import datetime
dt = datetime(2025, 1, 31, 14, 30, 15)
print(dt.strftime("%Y-%m-%d")) # 2025-01-31
print(dt.strftime("%A, %B %d, %Y")) # Friday, January 31, 2025
print(dt.strftime("%I:%M %p")) # 02:30 PMParsing with strptimestrptime
strptimestrptime (“string parse time”) does the reverse — it reads a string into a datetimedatetime, given a format that describes its layout.
from datetime import datetime
s = "2025-01-31 14:30"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M")
print(dt) # 2025-01-31 14:30:00
print(type(dt)) # <class 'datetime.datetime'>
# ISO strings have a dedicated fast parser:
print(datetime.fromisoformat("2025-01-31T14:30:15"))from datetime import datetime
s = "2025-01-31 14:30"
dt = datetime.strptime(s, "%Y-%m-%d %H:%M")
print(dt) # 2025-01-31 14:30:00
print(type(dt)) # <class 'datetime.datetime'>
# ISO strings have a dedicated fast parser:
print(datetime.fromisoformat("2025-01-31T14:30:15"))Date arithmetic with timedeltatimedelta
A timedeltatimedelta is a span of time. Add or subtract it from dates; subtract two dates to get a timedeltatimedelta.
from datetime import date, datetime, timedelta
today = date(2025, 1, 31)
week_later = today + timedelta(days=7)
print(week_later) # 2025-02-07
start = datetime(2025, 1, 1, 9, 0)
end = datetime(2025, 1, 1, 17, 30)
worked = end - start
print(worked) # 8:30:00
print(worked.total_seconds()) # 30600.0
# timedelta accepts weeks, days, hours, minutes, seconds
print(timedelta(weeks=2, days=3)) # 17 days, 0:00:00from datetime import date, datetime, timedelta
today = date(2025, 1, 31)
week_later = today + timedelta(days=7)
print(week_later) # 2025-02-07
start = datetime(2025, 1, 1, 9, 0)
end = datetime(2025, 1, 1, 17, 30)
worked = end - start
print(worked) # 8:30:00
print(worked.total_seconds()) # 30600.0
# timedelta accepts weeks, days, hours, minutes, seconds
print(timedelta(weeks=2, days=3)) # 17 days, 0:00:00Time zones (aware vs naive)
A datetime with no time zone is naive; one carrying tzinfotzinfo is aware. Prefer aware datetimes for anything crossing zones. Since Python 3.9 the standard library includes zoneinfozoneinfo.
from datetime import datetime, timezone, timedelta
from zoneinfo import ZoneInfo
# UTC-aware "now"
now_utc = datetime.now(timezone.utc)
print(now_utc.tzinfo) # UTC
# Convert between zones
ny = datetime(2025, 1, 31, 12, 0, tzinfo=ZoneInfo("America/New_York"))
tokyo = ny.astimezone(ZoneInfo("Asia/Tokyo"))
print(tokyo) # 2025-02-01 02:00:00+09:00from datetime import datetime, timezone, timedelta
from zoneinfo import ZoneInfo
# UTC-aware "now"
now_utc = datetime.now(timezone.utc)
print(now_utc.tzinfo) # UTC
# Convert between zones
ny = datetime(2025, 1, 31, 12, 0, tzinfo=ZoneInfo("America/New_York"))
tokyo = ny.astimezone(ZoneInfo("Asia/Tokyo"))
print(tokyo) # 2025-02-01 02:00:00+09:00The timetime module
Lower-level utilities tied to the operating system clock.
| Function | Purpose |
|---|---|
time.time()time.time() | Seconds since the epoch (a float timestamp). |
time.sleep(secs)time.sleep(secs) | Pause execution. |
time.localtime()time.localtime() | Convert a timestamp to a local struct_timestruct_time. |
time.gmtime()time.gmtime() | Convert a timestamp to a UTC struct_timestruct_time. |
time.strftime(fmt, t)time.strftime(fmt, t) | Format a struct_timestruct_time. |
time.perf_counter()time.perf_counter() | High-resolution timer for benchmarking. |
import time
start = time.perf_counter()
time.sleep(0.1) # pause 100 ms
elapsed = time.perf_counter() - start
print(f"Slept for {elapsed:.3f}s") # Slept for 0.100s
ts = time.time()
print(time.strftime("%Y-%m-%d", time.localtime(ts)))import time
start = time.perf_counter()
time.sleep(0.1) # pause 100 ms
elapsed = time.perf_counter() - start
print(f"Slept for {elapsed:.3f}s") # Slept for 0.100s
ts = time.time()
print(time.strftime("%Y-%m-%d", time.localtime(ts)))Use
perf_counter()perf_counter()to measure durations, andtime()time()to get a timestamp. Never usetime()time()for benchmarking — it can jump if the system clock changes.
The calendarcalendar module
Helpers for reasoning about months, weeks, and leap years.
| Function | Returns |
|---|---|
calendar.isleap(year)calendar.isleap(year) | TrueTrue if the year is a leap year. |
calendar.monthrange(y, m)calendar.monthrange(y, m) | (first_weekday, num_days)(first_weekday, num_days) for that month. |
calendar.weekday(y, m, d)calendar.weekday(y, m, d) | Weekday index (Mon=0). |
calendar.month(y, m)calendar.month(y, m) | A text calendar for the month. |
calendar.day_name[i]calendar.day_name[i] | Full weekday name for index ii. |
import calendar
print(calendar.isleap(2024)) # True
print(calendar.monthrange(2025, 2)) # (5, 28) -> starts Sat, 28 days
print(calendar.day_name[0]) # Monday
# Print a whole month grid
print(calendar.month(2025, 1))import calendar
print(calendar.isleap(2024)) # True
print(calendar.monthrange(2025, 2)) # (5, 28) -> starts Sat, 28 days
print(calendar.day_name[0]) # Monday
# Print a whole month grid
print(calendar.month(2025, 1))Putting it together
from datetime import date
def days_until_birthday(birth_month, birth_day):
today = date.today()
this_year = date(today.year, birth_month, birth_day)
next_bday = this_year if this_year >= today else date(today.year + 1, birth_month, birth_day)
return (next_bday - today).days
print(days_until_birthday(12, 25)) # days until the next Dec 25from datetime import date
def days_until_birthday(birth_month, birth_day):
today = date.today()
this_year = date(today.year, birth_month, birth_day)
next_bday = this_year if this_year >= today else date(today.year + 1, birth_month, birth_day)
return (next_bday - today).days
print(days_until_birthday(12, 25)) # days until the next Dec 25Common pitfalls
- Mixing naive and aware datetimes raises
TypeErrorTypeErrorwhen you subtract them. Pick one and stick with it. weekday()weekday()vsisoweekday()isoweekday()— one starts Monday at 0, the other at 1.strptimestrptimeformat must match exactly, including separators, or it raisesValueErrorValueError.time.sleeptime.sleepblocks the whole thread — fine for scripts, but use async patterns in servers.
Practice Exercises
Exercise 1 – Format today’s date
Exercise 2 – Days between two dates
Exercise 3 – Is it a leap year?
Summary
- Use
datetimedatetimefor dates, times, durations (timedeltatimedelta), and arithmetic. - Format with
strftimestrftime, parse withstrptimestrptime/fromisoformatfromisoformat. - Prefer aware datetimes (
timezone.utctimezone.utc,zoneinfo.ZoneInfozoneinfo.ZoneInfo) when zones matter. timetimegives timestamps, sleeping, andperf_counterperf_counterfor benchmarks.calendarcalendaranswers leap-year, weekday, and month-grid questions.
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
