<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/python-how-time-machine-is-o-1-where-freezegun-is-o-n--9nqxc2zke" -->

---
title: Python: how time-machine is O(1) where freezegun is O(n)
description: A deep-dive comparison of two Python time-mocking libraries — time-machine and freezegun — explaining why time-machine is O(1) while freezegun is O(n)....
canonical: https://daily.dev/posts/python-how-time-machine-is-o-1-where-freezegun-is-o-n--9nqxc2zke
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Python: how time-machine is O(1) where freezegun is O(n) | daily.dev
og:description: A deep-dive comparison of two Python time-mocking libraries — time-machine and freezegun — explaining why time-machine is O(1) while freezegun is O(n)....
og:url: https://daily.dev/posts/python-how-time-machine-is-o-1-where-freezegun-is-o-n--9nqxc2zke
og:image: https://api.daily.dev/og/posts/9Nqxc2zKE.png
og:image:alt: Python: how time-machine is O(1) where freezegun is O(n)
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Python: how time-machine is O(1) where freezegun is O(n)

**[Adam Johnson](https://daily.dev/sources/adamj)** · 10 min read · 0 upvotes · 0 comments

## Summary

A deep-dive comparison of two Python time-mocking libraries — time-machine and freezegun — explaining why time-machine is O(1) while freezegun is O(n). Benchmarks show freezegun's mock/unmock cycle grows linearly with the number of loaded modules (about 2.5 µs per module), while time-machine stays constant at ~1.5 µs regardless of project size. At 16,000 modules, time-machine is 27,300x faster. The difference comes down to approach: freezegun scans all sys.modules to rebind references, while time-machine directly overwrites C function pointers, affecting every reference at the C layer. The post also covers freezegun's leakiness (missed class attributes, closures, changed types) and introduces a migration CLI to automate switching from freezegun to time-machine.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://adamj.eu/tech/2026/08/03/python-time-machine-o1-freezegun-on>

## Questions this post answers

### Why is freezegun slower than time-machine for mocking time in Python tests?

freezegun scans every loaded module in sys.modules each time it freezes time, searching for references to the original date and time functions to replace, making its cost proportional to the number of module-level attributes across all loaded modules (O(n)). time-machine instead overwrites the C function pointer behind built-ins like time.time once, giving constant O(1) runtime of about 1.5 microseconds regardless of project size.

_Comparing test-mocking libraries like these is easier when tracking performance trade-offs on daily.dev._

### How much slower does freezegun get as a Python project grows in size?

freezegun's per-cycle cost grows linearly, at roughly 1.4 milliseconds fixed cost plus 2.5 microseconds per loaded module, while time-machine stays flat at 1.5 microseconds regardless of module count. Measured on Python 3.15 with freezegun 1.5.5 and time-machine 3.3.0, freezegun was 940 times slower at 259 modules but 27,300 times slower at 16,259 modules, meaning a 2,000-test suite could spend 82 seconds versus 3 milliseconds on time mocking alone.

_Developers weighing test suite speed can follow benchmarks like this one on daily.dev._

### How do I migrate my Python tests from freezegun to time-machine?

time-machine ships a migration CLI, runnable via `uvx --from 'time-machine[cli]' python -m time_machine migrate tests/test_file.py`, which rewrites the freezegun import, decorator, and context manager calls to time_machine.travel equivalents. Because freezegun freezes time by default while time-machine ticks by default, the tool adds `tick=False` to preserve behavior; running from a clean commit and using Ruff's F401/F821 rules helps catch any leftover partial rewrites.

_Teams planning a freezegun-to-time-machine migration can track tooling tips like this on daily.dev._

## Similar posts on daily.dev

- [Python: time-machine 3.3.0 lets your tests time-travel even faster](https://daily.dev/posts/python-time-machine-3-3-0-lets-your-tests-time-travel-even-faster-wjolqvp22) · Adam Johnson · 0 upvotes · 0 comments
- [Unit testing with wrapture](https://daily.dev/posts/unit-testing-with-wrapture-8myueoz61) · Planet Python · 0 upvotes · 0 comments

---

Tags: [#python](https://daily.dev/tags/python), [#testing](https://daily.dev/tags/testing)

[View this post on daily.dev](https://daily.dev/posts/python-how-time-machine-is-o-1-where-freezegun-is-o-n--9nqxc2zke)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Python: how time-machine is O(1) where freezegun is O(n)","url":"https://daily.dev/posts/python-how-time-machine-is-o-1-where-freezegun-is-o-n--9nqxc2zke","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/python-how-time-machine-is-o-1-where-freezegun-is-o-n--9nqxc2zke"},"datePublished":"2026-08-03T21:07:13.004Z","dateModified":"2026-09-14T08:43:37.872Z","description":"A deep-dive comparison of two Python time-mocking libraries — time-machine and freezegun — explaining why time-machine is O(1) while freezegun is O(n)....","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/cd5b1c2671abbb23f6fb59e0d5cbc3ad?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/cd5b1c2671abbb23f6fb59e0d5cbc3ad?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Adam Johnson","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Adam Johnson","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/bf70f4401c284aa5b628d1966029342c","url":"https://daily.dev/sources/adamj"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/python-how-time-machine-is-o-1-where-freezegun-is-o-n--9nqxc2zke","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"python,testing","timeRequired":"PT10M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Adam Johnson","item":"https://daily.dev/sources/adamj"},{"@type":"ListItem","position":3,"name":"Python: how time-machine is O(1) where freezegun is O(n)"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/python-how-time-machine-is-o-1-where-freezegun-is-o-n--9nqxc2zke#faq","mainEntity":[{"@type":"Question","name":"Why is freezegun slower than time-machine for mocking time in Python tests?","acceptedAnswer":{"@type":"Answer","text":"freezegun scans every loaded module in sys.modules each time it freezes time, searching for references to the original date and time functions to replace, making its cost proportional to the number of module-level attributes across all loaded modules (O(n)). time-machine instead overwrites the C function pointer behind built-ins like time.time once, giving constant O(1) runtime of about 1.5 microseconds regardless of project size. Comparing test-mocking libraries like these is easier when tracking performance trade-offs on daily.dev."}},{"@type":"Question","name":"How much slower does freezegun get as a Python project grows in size?","acceptedAnswer":{"@type":"Answer","text":"freezegun's per-cycle cost grows linearly, at roughly 1.4 milliseconds fixed cost plus 2.5 microseconds per loaded module, while time-machine stays flat at 1.5 microseconds regardless of module count. Measured on Python 3.15 with freezegun 1.5.5 and time-machine 3.3.0, freezegun was 940 times slower at 259 modules but 27,300 times slower at 16,259 modules, meaning a 2,000-test suite could spend 82 seconds versus 3 milliseconds on time mocking alone. Developers weighing test suite speed can follow benchmarks like this one on daily.dev."}},{"@type":"Question","name":"How do I migrate my Python tests from freezegun to time-machine?","acceptedAnswer":{"@type":"Answer","text":"time-machine ships a migration CLI, runnable via `uvx --from 'time-machine[cli]' python -m time_machine migrate tests/test_file.py`, which rewrites the freezegun import, decorator, and context manager calls to time_machine.travel equivalents. Because freezegun freezes time by default while time-machine ticks by default, the tool adds `tick=False` to preserve behavior; running from a clean commit and using Ruff's F401/F821 rules helps catch any leftover partial rewrites. Teams planning a freezegun-to-time-machine migration can track tooling tips like this on daily.dev."}}]}
```

