Python’s time module provides access to the system clock and time conversion utilities. import time. time: time.time() → float seconds since Unix epoch (UTC). time_ns: time.time_ns() → int nanoseconds since epoch. monotonic: time.monotonic() → float; never goes backwards; ideal for measuring elapsed time. perf_counter: time.perf_counter() → highest available resolution clock; for benchmarking. process_time: time.process_time() → CPU time of current process only (no sleep). sleep: time.sleep(secs) — block for at least secs seconds (float). gmtime: time.gmtime(epoch) → struct_time in UTC; omit epoch for now. localtime: time.localtime(epoch) → struct_time in local time. mktime: time.mktime(st) → epoch float from local struct_time. strftime: time.strftime("%Y-%m-%d %H:%M:%S", st) → string. strptime: time.strptime("2025-01-01", "%Y-%m-%d") → struct_time. asctime: time.asctime(st) → ‘Mon Jan 1 00:00:00 2025’. ctime: time.ctime(epoch) → human readable. timezone: time.timezone → offset of local non-DST zone in seconds west of UTC. altzone: DST offset. daylight: non-zero if DST zone defined. tzname: time.tzname → (‘EST’, ‘EDT’). clock_gettime: time.clock_gettime(time.CLOCK_REALTIME). get_clock_info: time.get_clock_info("monotonic") → namespace with implementation/resolution/adjustable. Claude Code generates timing decorators, rate limiters, elapsed loggers, and retry-with-backoff utilities.
CLAUDE.md for time
## time Stack
- Stdlib: import time
- Epoch: time.time() # float seconds UTC
- Mono: time.monotonic() # elapsed, never backwards
- Bench: time.perf_counter() # highest resolution
- CPU: time.process_time() # no sleep time
- Sleep: time.sleep(0.1)
- Format: time.strftime("%Y-%m-%d", time.localtime())
- Parse: time.strptime("2025-01-01", "%Y-%m-%d")
time Measurement and Conversion Pipeline
# app/timeutil.py — elapsed, benchmark, rate-limit, retry, format, timezone
from __future__ import annotations
import time
import math
import threading
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Any, Callable, Generator, TypeVar
T = TypeVar("T")
# ─────────────────────────────────────────────────────────────────────────────
# 1. Elapsed time and benchmarking
# ─────────────────────────────────────────────────────────────────────────────
@contextmanager
def elapsed(label: str = "", print_result: bool = True) -> Generator[list[float], None, None]:
"""
Context manager that measures wall-clock elapsed time.
Yields a one-element list; list[0] holds the duration after exit.
Example:
with elapsed("compress") as t:
some_work()
print(t[0]) # seconds as float
"""
result: list[float] = [0.0]
start = time.perf_counter()
try:
yield result
finally:
result[0] = time.perf_counter() - start
if print_result and label:
print(f"[{label}] {result[0]*1000:.3f} ms")
@dataclass
class BenchResult:
label: str
n: int
total_s: float
mean_s: float
min_s: float
max_s: float
std_s: float
def __str__(self) -> str:
return (
f"{self.label}: n={self.n} "
f"mean={self.mean_s*1e6:.2f}µs "
f"min={self.min_s*1e6:.2f}µs "
f"max={self.max_s*1e6:.2f}µs"
)
def bench(fn: Callable, n: int = 1000, label: str = "") -> BenchResult:
"""
Benchmark fn() by running it n times; return BenchResult with stats.
Example:
r = bench(lambda: sum(range(1000)), n=5000, label="sum")
print(r)
"""
lbl = label or getattr(fn, "__name__", "fn")
times: list[float] = []
for _ in range(n):
t0 = time.perf_counter()
fn()
times.append(time.perf_counter() - t0)
total = sum(times)
mean = total / n
mn = min(times)
mx = max(times)
variance = sum((t - mean) ** 2 for t in times) / n
return BenchResult(
label=lbl, n=n, total_s=total,
mean_s=mean, min_s=mn, max_s=mx, std_s=math.sqrt(variance),
)
def cpu_bench(fn: Callable, n: int = 1000, label: str = "") -> dict[str, float]:
"""
Measure CPU time (excludes sleep/IO) for fn() over n runs.
Example:
r = cpu_bench(heavy_compute, n=100)
print(r["mean_us"])
"""
lbl = label or getattr(fn, "__name__", "fn")
times: list[float] = []
for _ in range(n):
t0 = time.process_time()
fn()
times.append(time.process_time() - t0)
mean = sum(times) / n
return {"label": lbl, "n": n, "mean_us": mean * 1e6, "total_s": sum(times)}
# ─────────────────────────────────────────────────────────────────────────────
# 2. Timing decorators
# ─────────────────────────────────────────────────────────────────────────────
def timed(fn: Callable[..., T]) -> Callable[..., T]:
"""
Decorator that prints wall-clock time for each call.
Example:
@timed
def slow_op():
time.sleep(0.1)
slow_op() # prints: slow_op: 100.3 ms
"""
import functools
@functools.wraps(fn)
def wrapper(*args: Any, **kwargs: Any) -> T:
t0 = time.perf_counter()
result = fn(*args, **kwargs)
elapsed_ms = (time.perf_counter() - t0) * 1000
print(f"{fn.__name__}: {elapsed_ms:.3f} ms")
return result
return wrapper # type: ignore[return-value]
def timeout(seconds: float) -> Callable:
"""
Decorator that raises TimeoutError if fn takes longer than seconds.
Uses a background thread — fn must be thread-safe.
Example:
@timeout(1.0)
def might_hang():
time.sleep(2)
"""
import functools
def decorator(fn: Callable) -> Callable:
@functools.wraps(fn)
def wrapper(*args: Any, **kwargs: Any) -> Any:
result: list[Any] = [None]
exc: list[BaseException | None] = [None]
def target() -> None:
try:
result[0] = fn(*args, **kwargs)
except BaseException as e:
exc[0] = e
t = threading.Thread(target=target, daemon=True)
t.start()
t.join(seconds)
if t.is_alive():
raise TimeoutError(f"{fn.__name__} exceeded {seconds}s timeout")
if exc[0] is not None:
raise exc[0]
return result[0]
return wrapper
return decorator
# ─────────────────────────────────────────────────────────────────────────────
# 3. Rate limiting and retry
# ─────────────────────────────────────────────────────────────────────────────
class TokenBucket:
"""
Thread-safe token bucket rate limiter.
Example:
limiter = TokenBucket(rate=10, burst=20) # 10 ops/sec, burst up to 20
for item in items:
limiter.acquire()
process(item)
"""
def __init__(self, rate: float, burst: float | None = None) -> None:
self._rate = rate
self._burst = burst or rate
self._tokens = self._burst
self._last = time.monotonic()
self._lock = threading.Lock()
def acquire(self, tokens: float = 1.0) -> float:
"""Block until tokens available; return wait time in seconds."""
with self._lock:
now = time.monotonic()
elapsed_time = now - self._last
self._tokens = min(self._burst, self._tokens + elapsed_time * self._rate)
self._last = now
wait = 0.0
if self._tokens < tokens:
wait = (tokens - self._tokens) / self._rate
time.sleep(wait)
self._tokens = 0.0
else:
self._tokens -= tokens
return wait
def try_acquire(self, tokens: float = 1.0) -> bool:
"""Return True and consume tokens if available; False if not."""
with self._lock:
now = time.monotonic()
self._tokens = min(self._burst, self._tokens + (now - self._last) * self._rate)
self._last = now
if self._tokens >= tokens:
self._tokens -= tokens
return True
return False
def retry_with_backoff(
fn: Callable[[], T],
max_attempts: int = 3,
base_delay: float = 0.5,
backoff: float = 2.0,
jitter: bool = True,
) -> T:
"""
Call fn(), retrying on exception with exponential backoff.
Example:
result = retry_with_backoff(lambda: flaky_api_call(), max_attempts=4)
"""
import random
delay = base_delay
last_exc: BaseException | None = None
for attempt in range(max_attempts):
try:
return fn()
except Exception as exc:
last_exc = exc
if attempt < max_attempts - 1:
sleep_time = delay + (random.uniform(0, delay * 0.1) if jitter else 0)
time.sleep(sleep_time)
delay *= backoff
raise last_exc # type: ignore[misc]
# ─────────────────────────────────────────────────────────────────────────────
# 4. Time formatting and parsing
# ─────────────────────────────────────────────────────────────────────────────
def now_utc() -> float:
"""Return current Unix epoch as float."""
return time.time()
def now_utc_ns() -> int:
"""Return current Unix epoch in integer nanoseconds."""
return time.time_ns()
def format_epoch(epoch: float, fmt: str = "%Y-%m-%d %H:%M:%S", utc: bool = True) -> str:
"""
Format a Unix epoch float to a string.
Example:
format_epoch(0) # "1970-01-01 00:00:00"
format_epoch(time.time(), utc=False) # local time
"""
st = time.gmtime(epoch) if utc else time.localtime(epoch)
return time.strftime(fmt, st)
def parse_time(s: str, fmt: str = "%Y-%m-%d %H:%M:%S") -> float:
"""
Parse a time string to Unix epoch (interpreted as local time).
Example:
epoch = parse_time("2025-01-01 00:00:00")
"""
return time.mktime(time.strptime(s, fmt))
def human_duration(seconds: float) -> str:
"""
Return e.g. '2h 14m 33s' or '45.3ms' for a duration in seconds.
Example:
human_duration(3600) # "1h 0m 0s"
human_duration(0.045) # "45.0ms"
"""
if seconds < 1:
return f"{seconds*1000:.1f}ms"
if seconds < 60:
return f"{seconds:.3f}s"
m, s = divmod(int(seconds), 60)
h, m = divmod(m, 60)
if h:
return f"{h}h {m}m {s}s"
return f"{m}m {s}s"
def iso_utc(epoch: float | None = None) -> str:
"""
Return ISO-8601 UTC timestamp like '2025-01-01T12:00:00Z'.
Example:
iso_utc() # current time
iso_utc(0) # "1970-01-01T00:00:00Z"
"""
st = time.gmtime(epoch)
return time.strftime("%Y-%m-%dT%H:%M:%SZ", st)
# ─────────────────────────────────────────────────────────────────────────────
# 5. Stopwatch and interval scheduling
# ─────────────────────────────────────────────────────────────────────────────
class Stopwatch:
"""
Simple monotonic stopwatch with lap support.
Example:
sw = Stopwatch()
sw.start()
do_thing_a()
sw.lap("thing_a")
do_thing_b()
sw.lap("thing_b")
for name, t in sw.laps:
print(f" {name}: {human_duration(t)}")
"""
def __init__(self) -> None:
self._start: float | None = None
self._last: float = 0.0
self.laps: list[tuple[str, float]] = []
def start(self) -> "Stopwatch":
self._start = time.monotonic()
self._last = self._start
return self
def lap(self, name: str = "") -> float:
now = time.monotonic()
dt = now - self._last
self._last = now
self.laps.append((name or f"lap{len(self.laps)+1}", dt))
return dt
@property
def total(self) -> float:
if self._start is None:
return 0.0
return time.monotonic() - self._start
def reset(self) -> None:
self._start = None
self._last = 0.0
self.laps.clear()
@dataclass
class IntervalTimer:
"""
Fire a callback at regular intervals from a background thread.
Example:
timer = IntervalTimer(interval=1.0, fn=lambda: print("tick"))
timer.start()
time.sleep(5)
timer.stop()
"""
interval: float
fn: Callable[[], None]
_thread: threading.Thread = field(default=None, init=False, repr=False) # type: ignore[assignment]
_stop: threading.Event = field(default_factory=threading.Event, init=False, repr=False)
def start(self) -> None:
self._stop.clear()
def _run() -> None:
next_tick = time.monotonic() + self.interval
while not self._stop.is_set():
wait = next_tick - time.monotonic()
if wait > 0:
self._stop.wait(wait)
if not self._stop.is_set():
try:
self.fn()
except Exception:
pass
next_tick += self.interval
self._thread = threading.Thread(target=_run, daemon=True)
self._thread.start()
def stop(self) -> None:
self._stop.set()
if self._thread:
self._thread.join()
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== time demo ===")
print("\n--- epoch / format ---")
epoch = time.time()
print(f" time.time() = {epoch:.3f}")
print(f" time.time_ns() = {time.time_ns()}")
print(f" iso_utc() = {iso_utc(epoch)}")
print(f" format_epoch(0) = {format_epoch(0)}")
print(f" human_duration(7384.5) = {human_duration(7384.5)}")
print(f" human_duration(0.045) = {human_duration(0.045)}")
print("\n--- parse_time ---")
ep = parse_time("2025-06-15 12:00:00")
print(f" parse_time('2025-06-15 12:00:00') → {ep:.0f}")
print(f" round-trip: {format_epoch(ep, utc=False)}")
print("\n--- elapsed context manager ---")
with elapsed("sleep 0.05") as t:
time.sleep(0.05)
print(f" measured: {t[0]*1000:.1f} ms")
print("\n--- bench ---")
r = bench(lambda: sum(range(1000)), n=2000, label="sum_1k")
print(f" {r}")
print("\n--- stopwatch ---")
sw = Stopwatch().start()
time.sleep(0.02)
sw.lap("step_a")
time.sleep(0.03)
sw.lap("step_b")
for name, t_val in sw.laps:
print(f" {name}: {human_duration(t_val)}")
print(f" total: {human_duration(sw.total)}")
print("\n--- TokenBucket ---")
bucket = TokenBucket(rate=100, burst=5)
start_ts = time.monotonic()
for _ in range(5):
bucket.acquire()
print(f" 5 acquires at rate=100: {(time.monotonic()-start_ts)*1000:.1f} ms")
print("\n--- retry_with_backoff ---")
attempts = [0]
def flaky() -> str:
attempts[0] += 1
if attempts[0] < 3:
raise RuntimeError("not yet")
return "ok"
result = retry_with_backoff(flaky, max_attempts=5, base_delay=0.01)
print(f" result={result!r} after {attempts[0]} attempts")
print("\n--- IntervalTimer ---")
ticks: list[float] = []
timer = IntervalTimer(interval=0.05, fn=lambda: ticks.append(time.monotonic()))
timer.start()
time.sleep(0.22)
timer.stop()
print(f" ticked {len(ticks)} times in ~0.22s")
print("\n--- clock_info ---")
for name in ("time", "monotonic", "perf_counter", "process_time"):
info = time.get_clock_info(name)
print(f" {name:15s}: resolution={info.resolution:.2e}s adjustable={info.adjustable}")
print("\n=== done ===")
For the datetime alternative — datetime provides rich date-aware objects (datetime, date, timedelta, timezone) with arithmetic, formatting, ISO parsing, and timezone support; time provides lower-level access to the system clock, struct_time, and performance counters — use datetime when you need calendar-aware date math, timezone-correct scheduling, or ISO-8601 round-trips; use time when you need monotonic elapsed timing, high-resolution benchmarking, or direct POSIX clock access without object overhead. For the timeit alternative — timeit.timeit() runs a callable or statement string in a tight loop and auto-selects repeat counts to reduce measurement noise; it disables garbage collection during the run for fairer comparisons; time.perf_counter() is the underlying primitive that timeit uses internally — use timeit for micro-benchmark comparisons in REPL or scripts; use time.perf_counter() when you need inline timing inside application code, want to capture laps, or need to integrate with custom profiling infrastructure. The Claude Skills 360 bundle includes time skill sets covering elapsed()/bench()/cpu_bench() measurement helpers, timed()/timeout() decorators, TokenBucket rate limiter and retry_with_backoff() helpers, format_epoch()/parse_time()/human_duration()/iso_utc() formatting utilities, and Stopwatch/IntervalTimer classes. Start with the free tier to try time measurement patterns and time module pipeline code generation.