The time module provides various time-related functions for accessing the system clock, pausing execution, and measuring elapsed time. It works primarily with timestamps (seconds since the Unix epoch) and struct_time tuples, sitting at a lower level than the datetime module.
The system clock and sleeping
What you will learn: reading the current time as a timestamp, pausing with sleep(), and the relationship between time and datetime.
How to read this tab: time is the low-level layer; datetime is for calendar work. Learn when each applies.
The time module is Python's connection to the system clock. It tells you the current time as a number, lets your program pause (sleep), and measures how long things take. For calendar dates and human-friendly times, use datetime — time is the lower-level layer underneath.
time for timestamps, datetime for calendars. Use time for raw timestamps, sleeping, and performance. Use datetime for calendar dates, date arithmetic, and human formatting. Bridge them with datetime.fromtimestamp(time.time()).
Timestamps and sleeping
import time
# The current time as a Unix timestamp (seconds since 1 Jan 1970 UTC)
now = time.time()
print(now) # e.g. 1781900000.123 — a float
# Pause execution for a number of seconds
print("Starting...")
time.sleep(2) # wait 2 seconds (accepts floats: sleep(0.5))
print("...2 seconds later")
# Convert a timestamp to a readable local time string
print(time.ctime(now)) # e.g. 'Wed Jun 17 21:00:00 2026'
# Get the local time as a struct (year, month, day, hour, ...)
local = time.localtime(now)
print(local.tm_year, local.tm_mon, local.tm_mday) # 2026 6 17
print(local.tm_hour, local.tm_min) # 21 0Use time for timestamps, sleeping, and performance measurement. Use datetime for working with calendar dates, doing date arithmetic, and formatting dates for people. They interoperate: datetime.fromtimestamp(time.time()) bridges the two.
Never time durations with time.time(). The wall clock can jump backwards when the system clock is adjusted (NTP, DST), corrupting your measurement. Use time.perf_counter() — it is monotonic (never goes backwards) and high-resolution.
Measuring elapsed time
import time
# perf_counter — the RIGHT tool for timing code (high resolution)
start = time.perf_counter()
total = sum(range(10_000_000))
elapsed = time.perf_counter() - start
print(f"Took {elapsed:.4f} seconds")
# Do NOT use time.time() for measuring durations:
# it can jump backwards if the system clock is adjusted (NTP, DST).
# perf_counter is monotonic — it never goes backwards.
# A simple reusable timer pattern
def time_it(func, *args):
start = time.perf_counter()
result = func(*args)
return result, time.perf_counter() - start
result, duration = time_it(sum, range(1_000_000))
print(f"Result {result} in {duration:.4f}s")
# perf_counter_ns — nanosecond integer version (avoids float rounding)
start = time.perf_counter_ns()
x = 2 ** 1000
print(f"{time.perf_counter_ns() - start} nanoseconds")✅ Beginner tab complete
- I can read the current timestamp with time.time()
- I can pause execution with time.sleep() (accepts floats)
- I know time is for timestamps/sleep/timing; datetime is for calendar dates
- I can convert a timestamp with localtime() and ctime()
Measuring time and choosing a clock
What you will learn: perf_counter for benchmarking, formatting with strftime, and choosing between time/monotonic/perf_counter/process_time.
How to read this tab: Never measure durations with time.time() — it can jump backwards. Use perf_counter or monotonic.
Formatting and parsing time
The time module can format struct_time into strings and parse strings back, using the same directives as datetime's strftime.
import time
now = time.localtime()
# strftime — struct_time -> formatted string
print(time.strftime("%Y-%m-%d %H:%M:%S", now)) # '2026-06-17 21:00:00'
print(time.strftime("%A, %B %d", now)) # 'Wednesday, June 17'
# strptime — parse a string -> struct_time
parsed = time.strptime("2026-06-17", "%Y-%m-%d")
print(parsed.tm_year, parsed.tm_mon) # 2026 6
# gmtime — UTC instead of local time
utc = time.gmtime()
print(time.strftime("%Y-%m-%d %H:%M UTC", utc))
# mktime — struct_time (local) -> timestamp
ts = time.mktime(now)
print(ts) # back to a float timestamp
# timezone info
print(time.timezone) # seconds west of UTC (non-DST)
print(time.tzname) # ('IST', 'IST') or local zone names"What time is it" vs "how much time passed" need different clocks. time() answers the first (a timestamp that can jump). monotonic() and perf_counter() answer the second (durations that never go backwards). process_time() measures CPU time only, excluding sleep and I/O waits.
Choosing the right clock
import time
# time.time() — wall-clock time, can jump (NTP sync, DST, manual change)
# USE FOR: timestamps, "what time is it", logging when something happened
print(time.time())
# time.monotonic() — never goes backwards, no fixed reference point
# USE FOR: timeouts, rate limiting, "has 5 seconds passed?"
start = time.monotonic()
# ... do work ...
if time.monotonic() - start > 5:
print("timed out")
# time.perf_counter() — highest resolution, for benchmarking
# USE FOR: measuring how long code takes (most precise)
print(time.perf_counter())
# time.process_time() — CPU time of THIS process only (excludes sleep)
# USE FOR: measuring actual computation, ignoring I/O waits
start = time.process_time()
time.sleep(1) # process_time does NOT count sleep
x = sum(range(1_000_000)) # this DOES count
print(f"CPU time: {time.process_time() - start:.4f}s") # ~0.01, not ~1.0✅ Intermediate tab complete
- I use perf_counter() to measure how long code takes
- I never measure durations with time.time() (it can jump backwards)
- I can format and parse with strftime/strptime
- I know monotonic for timeouts, process_time for CPU-only timing
Clock characteristics and nanosecond precision
What you will learn: get_clock_info to inspect each clock, the _ns integer variants, and why float timestamps lose sub-microsecond precision.
How to read this tab: Read when precision matters or when choosing a clock programmatically.
Clock characteristics, resolution, and get_clock_info
Python exposes several distinct clocks because they answer different questions and have different guarantees. time.get_clock_info() reports the properties of each — whether it is monotonic, whether it is adjustable, and its resolution — letting you choose the right one programmatically and understand its precision on the current platform.
import time
# Inspect the properties of each clock
for name in ["time", "monotonic", "perf_counter", "process_time"]:
info = time.get_clock_info(name)
print(f"{name}: monotonic={info.monotonic}, "
f"adjustable={info.adjustable}, "
f"resolution={info.resolution}")
# time: monotonic=False, adjustable=True (wall clock — can jump)
# monotonic: monotonic=True, adjustable=False (never goes back)
# perf_counter: monotonic=True, adjustable=False (highest resolution)
# process_time: monotonic=True (CPU time only)
# thread_time — CPU time of the current THREAD (3.7+)
print(time.thread_time())
# The _ns variants return integer nanoseconds, avoiding float precision
# loss for very short or very long durations
print(time.time_ns()) # integer ns since epoch
print(time.monotonic_ns()) # integer ns, monotonic
print(time.perf_counter_ns()) # integer ns, highest resolution
# clock_gettime / CLOCK constants — direct POSIX clock access (Unix)
# import time
# print(time.clock_gettime(time.CLOCK_MONOTONIC))
# Why _ns matters: a float has 53 bits of mantissa. For timestamps
# that are billions of seconds, sub-microsecond precision is lost.
# Integer nanoseconds preserve full precision.
a = time.perf_counter_ns()
b = time.perf_counter_ns()
print(f"{b - a} ns elapsed") # exact integer differenceThe relationship between time and datetime is layered: datetime is built on the same underlying system clock that time.time() reads, but adds calendar arithmetic, timezone handling, and human-readable formatting. The practical division is clear — reach for time when you need raw timestamps, to pause execution, or to measure performance (where perf_counter and the _ns variants are the correct tools); reach for datetime when you are working with dates as calendar concepts. For asynchronous code, neither time.sleep nor blocking measurement belongs in a coroutine — asyncio provides its own non-blocking sleep and event-loop clock.
✅ Expert tab complete
- I can inspect clocks with time.get_clock_info()
- I know the _ns variants avoid float precision loss
- I know perf_counter and monotonic are non-adjustable; time() is adjustable
- I know async code uses asyncio.sleep, not time.sleep