🐍 Python Course · Stage 3 · Lesson 52 of 89 · time — Time & Clocks
Course Overview →

🔵 Read Beginner and Intermediate tabs. The Expert tab has the internals — worth reading once you finish Stage 3.

← sqlite3 — SQL Database shutil — File Operations →
thecodex.expert · The Codex Family of Knowledge
Python Standard Library

time — Time Access and Conversions

The time module provides low-level access to the system clock, sleeping, and performance measurement — the foundation beneath datetime.

import time docs.python.org Last verified:
Canonical Definition

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.

🟩 Beginner

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.

⏱ 25 min📄 2 sections🔶 Prerequisite: Modules & Packages
The idea

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

Pythonbasics.py
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 0
time vs datetime

Use 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

Pythonmeasuring.py
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()

Continue to shutil →

🔵 Intermediate

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.

⏱ 25 min📄 2 sections🔶 Prerequisite: Beginner tab

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.

Pythonformatting.py
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

Pythonclocks.py
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
Commonly confused
time.time() vs time.monotonic()/perf_counter(). Use time() for "what time is it" (timestamps). Use monotonic() or perf_counter() for "how much time has passed" (durations). time() can jump backwards when the system clock is adjusted, corrupting duration measurements.
sleep() is approximate. time.sleep(1) waits at least 1 second, but the OS may make it slightly longer. It is not precise for real-time scheduling, and it blocks the entire thread — in async code, use asyncio.sleep instead.

✅ 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

Continue to shutil →

🔴 Expert

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.

⏱ 20 min📄 1 section🔶 Prerequisite: After Stage 3

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.

Pythonclock_info.py
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 difference

The 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

Continue to shutil →

Sources

1
Python Standard Library — time. docs.python.org/3/library/time.html.
2
Python Standard Library — time.get_clock_info. docs.python.org/3/library/time.html#time.get_clock_info.
3
PEP 418 — Add monotonic time, performance counter, and process time functions. peps.python.org/pep-0418/.
4
PEP 564 — Add new time functions with nanosecond resolution. peps.python.org/pep-0564/.
Source confidence: High Last verified: Primary source: docs.python.org time