freezegun-python
Wraps freezegun (github.com/spulec/freezegun), the Python time-mocking library: @freeze_time decorator / context manager, freeze_time(...).start() + stop(), tick / move_to / tz_offset, and integration with datetime.now / time.time / time.localtime. Use when testing Python code that calls datetime / time. Composes dst-transition-reference + iso-8601-vs-rfc-3339-reference.
freezegun-python
Overview
freezegun is the canonical Python time-mocking library. Per github.com/spulec/freezegun, it patches datetime.datetime, datetime.date, time.time, time.gmtime, time.localtime, time.strftime, plus asyncio time across the test scope.
When to use
Authoring
Install
pip install freezegunDecorator (most common)
from freezegun import freeze_time
from datetime import datetime
@freeze_time("2026-05-20T14:30:00")
def test_today_is_may_20():
assert datetime.now().strftime("%Y-%m-%d") == "2026-05-20"Context manager
with freeze_time("2026-05-20T14:30:00"):
assert datetime.now().strftime("%Y-%m-%d") == "2026-05-20"Manual start/stop
freezer = freeze_time("2026-05-20T14:30:00")
freezer.start()
try:
# ...
finally:
freezer.stop()Tick mode
@freeze_time("2026-05-20T14:30:00", tick=True)
def test_clock_advances():
t1 = datetime.now()
# ... a few ops later
t2 = datetime.now()
assert t2 > t1tick=True lets real time pass from the frozen start point. Useful for tests that need duration measurement.
Move to a different time mid-test
@freeze_time("2026-05-20T14:30:00")
def test_advance_one_day(freezer):
assert datetime.now().day == 20
freezer.move_to("2026-05-21T14:30:00")
assert datetime.now().day == 21Or via freezer.tick(delta=timedelta(hours=24)).
Timezone offset
@freeze_time("2026-05-20T14:30:00", tz_offset=-5)
def test_eastern_time():
# datetime.now() returns wall-clock; datetime.utcnow() returns UTC
assert datetime.utcnow().hour == 19 # 14:30 + 5
assert datetime.now().hour == 14DST + zone tests
import pytest
from zoneinfo import ZoneInfo
@freeze_time("2026-03-08T07:30:00") # 02:30 EST OR 03:30 EDT — depends on resolution
def test_spring_forward_handling():
ny = datetime.now(ZoneInfo("America/New_York"))
# Asserts against expected library behaviour per dst-transition-referenceAsync support
@freeze_time("2026-05-20T14:30:00")
async def test_async_now():
await asyncio.sleep(0)
assert datetime.now().strftime("%Y") == "2026"Per freezegun docs: "freezegun is compatible with asyncio."
Running
pytest tests/CI integration
jobs:
python-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
- run: pip install -e ".[test]" freezegun
- run: pytest tests/Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
freeze_time("2026-05-20") (date only) | freezegun interprets as midnight local; subtle | Use ISO datetime |
time.sleep(...) inside frozen-time block | Sleep is real-time; frozen clock doesn't advance | Use freezer.tick() |
Mock datetime.utcnow separately | Conflicts with freezegun | Let freezegun do both |
| Forget freezer cleanup in fixtures | Cross-test contamination | Use decorator or with |
Test DST without tz_offset or zoneinfo | Result is UTC; misses local behaviour | Combine with zoneinfo |
@freeze_time on a class without decorate_class=True | Methods not patched | Use class decorator explicitly |
| Test third-party C extensions calling system time | freezegun only patches Python-level APIs | Use libfaketime |