mrkeyoor.com_
Sun 20 Sept 17:55 UTC
PyPITestingupdated 20 Sept 2026

freezegun review

Freezegun 1.5.5 imported in 0.32 seconds after our 0.2-second Python 3.12 install. During a decorator, context manager, or manually started freeze, it replaces reads from datetime, date, time.time, monotonic clocks, and performance counters. That makes expiry, leap-day, and billing-boundary tests independent of the wall clock. The current 1.5.5 release repairs parametrized test calls that use `func` as an argument name after 1.5.4 broke that case.

Verdict

Freezegun 1.5.5 installed in 0.2 seconds and used 1 MB across 3 packages in our sandbox, with 0 pip-audit findings. It fits Python-only clock tests; external clocks need another tool, and asyncio deadlines require `real_asyncio=True`.

We installed it

Lab card: what happened when we installed freezegunScreenshot of freezegun documentation
Install✓ · 0.2s3 packages on disk · 1 MB
Importimport freezegun in 0.32s · pure Python · py.typed · requires Python >=3.8
Known vulns0(pip-audit)

Answers from our run

Does freezegun install cleanly?

Yes. In a fresh container with an empty cache, pip install freezegun finished in 0.2s, leaving 3 packages and 1 MB on disk. pip-audit reported no known vulnerabilities.

What does freezegun need to run?

Python >=3.8, and nothing compiled: it is pure Python. In our run import freezegun succeeded in 0.32s, and the package ships py.typed for type checkers.

freezegun or time-machine: which should you use?

time-machine: Use it when freeze startup cost matters enough to prefer CPython-level patching and a compiled extension is acceptable. Freezegun 1.5.5 installed in 0.2 seconds and used 1 MB across 3 packages in our sandbox, with 0 pip-audit findings.

When should you not use freezegun?

Walk away if the clock comes from PostgreSQL, Redis, a child process, or a native extension. Python reference patching cannot change those sources.

API stability4/5The `freeze_time` entry point still supports decorator, context-manager, and explicit start/stop use, plus tick, move_to, offset, injection, and asyncio options. Its central shape has held, but integrations are less settled: 1.5.3 addressed pytest 8.4 fixtures, 1.5.4 addressed yielded fixtures, and 1.5.5 repaired parametrized calls with an argument named `func`.
Docs4/5One README documents every public mode with code: decorators, context blocks, manual lifetime, offsets, generator or callable dates, tick controls, move_to, asyncio handling, ignore prefixes, and the full signature. It warns about definition-time defaults and arbitrary class decoration. A 4 reflects the weak navigation and the lack of a separate explanation for patch scope or startup cost.
Maintenance2/5Version 1.5.5 reached PyPI on August 9, 2025, and GitHub shows the last repository push on August 19, 2025. The project is unarchived, but 167 issues and pull requests remain open and no code push appeared in the following year. Since the latest releases fixed pytest fixture and parametrization regressions, compatibility with future Python and pytest versions depends on maintenance resuming.
Ecosystem5/5The stored registry count is 13,235,712 weekly downloads, and GitHub reports 4,525 stars. Freezegun covers functions, unittest classes, pytest tests, coroutines, and modules that imported datetime directly; pytest-freezer adds a fixture wrapper around it. The ecosystem boundary is the process itself, since database clocks, native extensions, and subprocesses require a system-level alternative.

Use it if

  • Use it when existing code calls datetime.now or date.today directly and introducing an injectable clock across every caller would be a larger change.
  • Adopt it for expiry, renewal, leap-day, and offset tests whose expected result must stay fixed on developer machines and CI.
  • Choose it when a scenario should advance with tick or jump through move_to without waiting for real seconds or days.
  • Apply it to a unittest class or selected pytest functions when one date should cover several related assertions.
Skip it if

Setup reality

We installed freezegun 1.5.5 in 0.2 seconds inside a fresh Python 3.12 container. The result was 3 packages and 1 MB on disk. It is pure Python, has 1 direct dependency, includes py.typed, uses the Apache-2.0 license, and requires Python 3.8 or newer. import freezegun took 0.32 seconds, and pip-audit found 0 known vulnerabilities.

There is no compiler, credential, plugin hook, or config file to set up. freeze_time works directly as a decorator or context manager, and python-dateutil parses its date strings. The object returned by a context can tick by a duration or move to another instant. auto_tick_seconds advances after each patched read; tick=True instead lets real elapsed time continue from the chosen starting value.

Only references inside the Python process move. Database functions, subprocesses, and native code can still read the actual clock. Freezegun skips prefixes such as threading, Selenium, and parts of pytest by default. A per-freeze ignore list adds local exclusions. extend_ignore_list preserves built-ins, while default_ignore_list replaces them and can expose modules that the project normally protects.

Wall time, time.monotonic, and time.perf_counter freeze together. Asyncio sleeps and timeout machinery therefore need real_asyncio=True when the loop must keep its actual monotonic clock. Values captured in function defaults remain fixed at definition time. Class decoration wraps callable attributes and carries a README warning for arbitrary classes. Version 1.5.5 fixes the func parameter collision in parametrized tests.

Patterns

Hold one test at a fixed datetime freeze-test

from datetime import datetime
from freezegun import freeze_time

@freeze_time("2026-01-15 09:30:00")
def test_cutoff():
    assert datetime.now() == datetime(2026, 1, 15, 9, 30)

Supported clock calls report January 15 only while the decorated test runs. Freezegun restores their original references after the call.

Freeze time inside one context block freeze-context

from datetime import date
from freezegun import freeze_time

with freeze_time("2024-02-29"):
    assert date.today().isoformat() == "2024-02-29"

Only the with block sees February 29, 2024. Setup and cleanup around it continue to read the machine clock.

Advance a frozen clock by a duration advance-clock

from datetime import datetime, timedelta
from freezegun import freeze_time

with freeze_time("2026-01-01") as frozen:
    frozen.tick(delta=timedelta(days=30))
    assert datetime.now() == datetime(2026, 1, 31)

A 30-day tick changes wall time and the patched monotonic clock together, and returns the resulting frozen datetime.

Move directly to another calendar date jump-to-date

from datetime import date
from freezegun import freeze_time

with freeze_time("2026-03-01") as frozen:
    frozen.move_to("2026-04-01")
    assert date.today().isoformat() == "2026-04-01"

move_to accepts date and datetime objects or the same parseable strings accepted by freeze_time.

Add 15 seconds after each time read auto-advance

from datetime import datetime, timedelta
from freezegun import freeze_time

with freeze_time("2026-01-01", auto_tick_seconds=15):
    first = datetime.now()
    second = datetime.now()
    assert second == first + timedelta(seconds=15)

auto_tick_seconds overrides tick behavior. A debug log or extra datetime.now call also advances the result by 15 seconds.

Run from an artificial starting instant keep-clock-running

from datetime import datetime
from freezegun import freeze_time

with freeze_time("2020-01-01", tick=True):
    assert datetime.now() >= datetime(2020, 1, 1)

With tick enabled, elapsed real duration is added to January 1, 2020. The value therefore changes during the block.

Keep asyncio monotonic time alive test-asyncio

import asyncio
from datetime import datetime
from freezegun import freeze_time

@freeze_time("2026-01-01", real_asyncio=True)
async def test_timeout_path():
    await asyncio.sleep(0)
    assert datetime.now() == datetime(2026, 1, 1)

real_asyncio leaves the loop's monotonic source real while wall-clock APIs continue to report January 1, 2026.

Shift local time by a fixed offset apply-timezone-offset

from datetime import datetime, date
from freezegun import freeze_time

with freeze_time("2026-01-01 02:00:00", tz_offset=-4):
    assert datetime.utcnow() == datetime(2026, 1, 1, 2)
    assert date.today().isoformat() == "2025-12-31"

tz_offset moves local readings 4 hours behind frozen UTC. It has none of the daylight-saving transitions of a named zone.

Inject the freezer as a named argument receive-freezer

from datetime import date
from freezegun import freeze_time

@freeze_time("2026-01-01", as_kwarg="clock")
def test_renewal(clock):
    clock.move_to("2027-01-01")
    assert date.today().year == 2027

The chosen keyword must not collide with another argument. Version 1.5.5 specifically repairs a collision with the name func.

Start and stop a freezer yourself start-stop-manually

from freezegun import freeze_time

freezer = freeze_time("2026-05-01")
try:
    freezer.start()
    run_scenario()
finally:
    freezer.stop()

The finally block restores patched references even when run_scenario raises. Omitting stop can contaminate every later test in the process.

Exclude a module prefix from patching ignore-module

from freezegun import freeze_time

with freeze_time("2026-01-01", ignore=["vendor.metrics"]):
    run_job()

Each ignore value matches a module-name prefix. vendor.metrics can therefore keep measuring real elapsed time during this freeze.

Extend exclusions for the whole test process extend-ignore-list

import freezegun

freezegun.configure(extend_ignore_list=["tensorflow", "vendor.metrics"])

extend_ignore_list keeps the package defaults and adds 2 prefixes. default_ignore_list discards every built-in exclusion instead.

Alternatives

PackageRegistryPick it when
time-machinePyPIUse it when freeze startup cost matters enough to prefer CPython-level patching and a compiled extension is acceptable.
pytest-freezerPyPIUse it when the suite wants a pytest fixture API while retaining Freezegun as the underlying clock engine.
libfaketimePyPIUse it on supported systems when child processes and native clock calls must observe the same artificial time.

More testing guides

pytest · chai · vitest · jsdom · playwright · coverage · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.