Changelog¶
3.5.1 (2026-09-08)¶
Only pass the compiler option
-mno-omit-leaf-frame-pointerwhen the compiler supports it, checked with a tiny probe program. The option is unsupported outside of x86 and ARM64, so this fixes building from source on other architectures, like PowerPC, which failed since the option was added in version 3.3.0.PR #692. Thanks to Colin Watson for pointing this out and linking to the workaround in Debian Bug #1146407.
Fix the mocked
datetime.date.today()anddatetime.datetime.today()to be exact for all supported dates, likedatetime.datetime.now(). Previously, they went through a floating-point timestamp, which could round the microseconds, or even the whole day, for dates far in the future.Fix the class decorator to stop time travelling when
tearDownClass()raises an exception, or whensetUpClass()raises an exception not deriving fromException, such as the skip outcome frompytest.skip(). Previously, time remained mocked for the rest of the process in these cases.Fix
Traveller.move_to()to keep the current timezone mocked when the given destination is unsupported. Previously, the timezone was restored before the destination was checked, leaving it unmocked whilst still time travelling.Fix the
time_machinepytest fixture to not try to stop a traveller that failed to start.Fix the mocked
time.strftime()to raiseTypeErrorwhen passedNoneas its second argument, like the unmocked function, rather than treating it as the current time.Fix the Migration CLI to keep the rewritten imports on the same line when a freezegun import shares its line with other code, like
if TYPE_CHECKING: from freezegun import freeze_time, FakeDate. Previously, the remainingfrom freezegun import FakeDatewas moved to a new line, outside the block.Extend the Migration CLI to migrate uses of
FrozenDateTimeFactoryin string annotations, likefreezer: "FrozenDateTimeFactory". Previously, the import was removed whilst such annotations were left referring to it.Fix the Migration CLI to not rename a
freezerargument when the function already has atime_machineargument, which produced invalid syntax.Fix the Migration CLI to report positions in the rewritten file, rather than the original. Previously, positions could be off when rewrites earlier in the file changed the number of lines, or the length of the same line.
Fix the Migration CLI to not report a
freezegunusage for migrated calls with a parenthesized module name, like(freezegun).freeze_time(...).Fix the Migration CLI to leave a
passstatement when removing every unusedFrozenDateTimeFactoryimport in a block, rather than producing invalid syntax.Make
TimeMachineFixtureimportable even when pytest isn’t installed. This avoids triggeringpossibly-missing-attributeorpossibly-missing-importviolations in ty for test files with annotations.Thanks to Nick Pope in PR #675.
3.5.0 (2026-08-25)¶
Support
Noneas a destination fortravel()andTraveller.move_to(), meaning the current time. Use this withtick=Falseto freeze time at the present moment, matching freezegun’s behaviour whenfreeze_time()is called with no arguments.Extend the Migration CLI to migrate aliased imports:
import freezegun as fgandfrom freezegun import freeze_time as ft, plus calls using such aliases.Extend the Migration CLI to migrate the
pytest.mark.freeze_timemarker in module-level and class-levelpytestmarkassignments.Extend the Migration CLI to migrate
freeze_time()calls and markers that passtz_offsetwith a literal zero value. The argument is dropped, since a zero offset has no effect.Extend the Migration CLI to migrate
freeze_time()calls and markers that passreal_asyncio. The argument is dropped, whatever its value, since time-machine does not mocktime.monotonic(), so asyncio event loops always see real time.Extend the Migration CLI to migrate
freeze_time()calls and markers with no destination argument, which freeze at the current time.Noneis added as the destination, using the new support forNonedestinations (above).Extend the Migration CLI to migrate
freeze_time()calls and markers that passignore. The argument is dropped, since it works around problems with freezegun’s module patching, which time-machine’s C-level mocking doesn’t have.Make the Migration CLI report freezegun-related usages that it recognizes but cannot migrate, with their positions, like:
example/tests.py:9:2: freeze_time usage not migratedThis makes it easier to find and fix the remaining usages manually.
Make the Migration CLI report files that cannot be parsed, rather than skipping them silently. This helps notice files using syntax from a newer Python version than the tool is run with.
Extend the Migration CLI to migrate “raw use” assignments of
freeze_time()to variables orself.attributes used withstart()andstop(), covering unittestsetUp()/tearDown()patterns likeself.freezer = freeze_time(...)withself.addCleanup(self.freezer.stop).Extend the Migration CLI to migrate imports and uses of freezegun’s
FrozenDateTimeFactoryclass, often used to annotate thefreezerfixture argument, to time-machine’s newly-documented equivalent,time_machine.TimeMachineFixture.
3.4.0 (2026-08-10)¶
Publish wheels for Python 3.15.
Thanks to Edgar Ramírez Mondragón in PR #655.
Fix a reference leak in the patched
datetime.datetime.utcnow()that occurred when its Python 3.12+DeprecationWarningwas raised as an error, such as under-W error. Each such call leaked a reference to the active traveller object.Extend the Migration CLI to migrate patterns from freezegun’s pytest plugins, pytest-freezegun and pytest-freezer: the
freezerfixture and thepytest.mark.freeze_timemarker. Also make it migratefreeze_time()context managers that bind the result withas, including convertingtick()method calls toshift().PR #658. Thanks to Javier Buzzi for the initial work in PR #562.
Fix the Migration CLI to handle trailing commas in
freeze_time()calls when addingtick=False. Previously, it produced invalid syntax, liketravel("2023-01-01",, tick=False).Fix the Migration CLI to not indent imports kept from a rewritten from-import when the import doesn’t start its line, such as after
if True:on one line. Previously, the kept import gained leading whitespace, producing invalid syntax.
3.3.1 (2026-08-04)¶
Fix small rounding errors in destination calculations.
Previously, time-machine computed destinations in floating-point seconds, which could lead to small rounding errors: up to a few hundred nanoseconds for present-day destinations, growing to several microseconds by the year 2500. This change fixes the pipeline to use integer nanoseconds throughout, so time-travel is precise for any supported date.
3.3.0 (2026-07-31)¶
Support Python 3.15.
Mock
datetime.date.today()directly, for Python 3.15 support.Previously time-machine was mocked only indirectly, since CPython implemented it by calling
cls.fromtimestamp(time.time()). Python 3.15 added a fast path that reads the system clock directly (CPython Issue #130980), so time travel no longer affected it, which the new mock fixes. Consequently, there are new escape hatch functions:escape_hatch.datetime.date.today()andescape_hatch.datetime.datetime.today().Thanks to Miro Hrončok and Karolina Surma for the report in Issue #610, Lumír ‘Frenzy’ Balhar for the fix in PR #618, and Maurycy Pawłowski-Wieroński for review
Support isolated subinterpreters, as created by
concurrent.interpreterson Python 3.14+. Previously, importing time-machine in an isolated subinterpreter failed with anImportError.Move the intermediary functions that patched functions call from Python to C. This change reduces the overhead of calling patched functions like
time.time()while time travelling, making them around 10-20% faster.Fix returning
datetimesubclasses from theirnow()andutcnow()methods while time travelling. Previously, time-machine would always return a plaindatetime.datetimeinstance, rather than the subclass.Fix hiding of the
DeprecationWarningfromdatetime.utcnow()on Python 3.12+ while time travelling. Previously, the mocked version of the function did not raise the warning at all, so deprecated calls could pass unnoticed in tests. The warning is attributed to the calling code, like the real function does.The escape hatch function
escape_hatch.datetime.datetime.utcnow()also now attributes its warning to the calling code, rather than to a line within time-machine.PR #649. Thanks to Tamir Duberstein for the report in Issue #445 and Anders Kaseorg for the initial implementation in PR #486.
Fix the standard
uuidlibrary functionsuuid1(),uuid6(), anduuid7()to correctly generate values for the destination when time travels backwards. These functions cache the timestamp of the value they generated most recently, and never generate a value stamped before it. Previously, after time-travelling backwards, these functions would keep generating values stamped for the cached timestamp, rather than the new destination. The caches are now reset whenever time travel starts, stops, or moves backwards.PR #648. Thanks to Diego Carrasco for the report in Issue #601 and initial work in PR #597.
Build with frame pointers enabled, preparation for PEP 831.
Stop shipping wheels for 32-bit Linux and Windows.
Stop shipping wheels for free-threaded Python 3.13 since cibuildwheel 4.0.0 dropped support for building them.
Extend the Migration CLI to update
freeze_time()calls that passtick.Thanks to George-Cristian Birzan for the report in Issue #609 and tanren for the fix in PR #636.
Extend the Migration CLI to rewrite
freeze_time()decorators on async functions.Thanks to George-Cristian Birzan for the report in Issue #608 and Sanjay Santhanam for the fix in PR #640.
Fix the Migration CLI to leave relative imports called
freezegunalone. Previously it rewrotefrom .freezegun import freeze_time, which imports from a local module that happens to share freezegun’s name.Extend the Migration CLI to rewrite from-imports that import
freeze_timealongside other names. For example,from freezegun import freeze_time, FakeDatenow becomesimport time_machineplusfrom freezegun import FakeDate.
3.2.0 (2025-12-17)¶
Add
time_machine.naive_modeto control how time-machine interprets naive datetimes.The default mode is
MIXED, which preserves existing behaviour: naivedatetimeobjects anddateobjects are interpreted as UTC, while naive datetime strings are interpreted as local time.Three alternative modes are available:
UTC: naive datetimes are always interpreted as UTC.LOCAL: naive datetimes are interpreted as local time, matching Python’s default semantics, and freezegun.ERROR: naive datetimes raise aRuntimeError, ensuring your tests are isolated from the current timezone.
Note
It’s recommended you use
LOCALorERRORto avoid confusion around naive datetimes.PR #591. Thanks to Paolo Melchiorre for review.
Thanks to PhML, Stefaan Lippens, Matthieu Rigal, Nikita Demir, Steve Mavens, Andy Freeland, and Paul Ganssle for their input on Issue #257.
Raise
RuntimeErrorwhen attempting to start time travelling if freezegun is active.This change should help avoid surprises when migrating complex test suites from freezegun to time-machine.
3.1.0 (2025-11-21)¶
Optimize patching of
uuidmodule. By avoiding usingunittest.mock, this small overhead from startingtime_machine.travel()has been reduced about 20x, from ~600ns to ~30ns by one benchmark.
3.0.0 (2025-11-18)¶
Remove mocking of
time.monotonic()andtime.monotonic_ns().This mocking caused too many issues, such as causing freezes in asyncio event loops (Issue #387), preventing pytest-durations from timing tests correctly (Issue #505), and triggering timeouts in psycopg (Issue #509). The root cause here is that mocking the monotonic clock breaks its contract, allowing it to move backwards when it’s meant to only move forwards.
As an alternative, use
unittest.mockto mock the monotonic function for the specific tested modules that need it. That means that your code should importmonotonic()ormonotonic_ns()directly, so that your tests can mock it in those places only. For example, if your system under test looks like:# example.py from time import monotonic def measurement(): start = monotonic() ... end = monotonic() return end - start
…then your tests can mock
monotonic()like this:from unittest import TestCase, mock import example class MeasurementTests(TestCase): def test_success(self): with mock.patch.object(example, "monotonic", side_effect=[0.0, 1.23]): result = example.measurement() assert result == 1.23
Parse
strdestinations withdatetime.fromisoformat()first, before falling back to dateutil if installed.datetime.fromisoformat()can parse most valid ISO 8601 formats, with better performance and no extra dependencies.Make the dependency on dateutil optional. To include dateutil support, install with the
dateutilextra:python -m pip install time-machine[dateutil]
Beware that some of the formats that dateutil parses are ambiguous and may lead to unexpected results.
Rename the
Coordinatesclass toTraveller, to match the recommended context manager variable name.Thanks to Matt Wang in PR #535.
Drop Python 3.9 support.
Make the
escape_hatchfunctions raiseValueErrorwhen called outside of time-travelling, rather than triggering segmentation faults.
2.19.0 (2025-08-19)¶
Add marker support to the pytest plugin. Decorate tests with
@pytest.mark.time_machine(<destination>)to set time during a test, affecting function-level fixtures as well.Thanks to Javier Buzzi in PR #499.
Add asynchronous context manager support to
time_machine.travel(). You can now useasync with time_machine.travel(...):in asynchronous code, per the documentation.Import date and time functions once in the C extension.
This should improve speed a little bit, and avoid segmentation faults when the functions have been swapped out, such as when freezegun is in effect. (time-machine still won’t apply if freezegun is in effect.)
2.18.0 (2025-08-18)¶
Update the migration CLI to detect unittest classes based on whether they use
self.assert*methods likeself.assertEqual().Fix free-threaded Python warning:
RuntimeWarning: The global interpreter lock (GIL) has been enabled...as seen on Python 3.13+.Thanks to Javier Buzzi in PR #531.
Add support to
travel()fordatetimedestinations withtzinfoset todatetime.UTC(datetime.timezone.utc).Thanks to Lawrence Law in PR #502.
Prevent segmentation faults in unlikely scenarios, such as if the
time_machinemodule cannot be imported.Make
travel()fully unpatch date and time functions when travel ends. This may fix certain edge cases.
2.17.0 (2025-08-05)¶
Include wheels for Python 3.14.
Thanks to Edgar Ramírez Mondragón in PR #521.
Support free-threaded Python.
Thanks to Javier Buzzi in PR #500.
Add a new CLI for migrating code from freezegun to time-machine.
Install with
pip install time-machine[cli]and run withpython -m time_machine migrate.See more in the documentation.
Move the documentation to Read the Docs, and add a retro-futuristic logo.
2.16.0 (2024-10-08)¶
Drop Python 3.8 support.
2.15.0 (2024-08-06)¶
Include wheels for Python 3.13.
2.14.2 (2024-06-29)¶
Fix
SystemErroron Python 3.13 and Windows when starting time travelling.Thanks to Bernát Gábor for the report in Issue #456.
2.14.1 (2024-03-22)¶
Fix segmentation fault when the first
travel()call in a process uses atimedelta.Thanks to Marcin Sulikowski for the report in Issue #431.
2.14.0 (2024-03-03)¶
Fix
utcfromtimestamp()warning on Python 3.12+.Thanks to Konstantin Baikov in PR #424.
Fix class decorator for classmethod overrides.
Thanks to Pavel Bitiukov for the reproducer in PR #404.
Avoid calling deprecated
uuid._load_system_functions()on Python 3.9+.Thanks to Nikita Sobolev for the ping in CPython Issue #113308.
Support Python 3.13 alpha 4.
Thanks to Miro Hrončok in PR #409.
2.13.0 (2023-09-19)¶
Add support for
datetime.timedeltatotime_machine.travel().Thanks to Nate Dudenhoeffer in PR #298.
Fix documentation about using local time for naive date(time) strings.
Thanks to Stefaan Lippens in PR #306.
Add
shift()method to thetime_machinepytest fixture.Thanks to Stefaan Lippens in PR #312.
Mock
time.monotonic()andtime.monotonic_ns(). They return the values oftime.time()andtime.time_ns()respectively, rather than real monotonic clocks.Thanks to Anthony Sottile in PR #382.
2.12.0 (2023-08-14)¶
Include wheels for Python 3.12.
2.11.0 (2023-07-10)¶
Drop Python 3.7 support.
2.10.0 (2023-06-16)¶
Support Python 3.12.
2.9.0 (2022-12-31)¶
Build Windows ARM64 wheels.
Explicitly error when attempting to install on PyPy.
Thanks to Michał Górny in PR #315.
2.8.2 (2022-09-29)¶
Improve type hints for
time_machine.travel()to preserve the types of the wrapped function/coroutine/class.
2.8.1 (2022-08-16)¶
Actually build Python 3.11 wheels.
2.8.0 (2022-08-15)¶
Build Python 3.11 wheels.
2.7.1 (2022-06-24)¶
Fix usage of
ZoneInfofrom thebackports.zoneinfopackage. This makesZoneInfosupport work for Python < 3.9.
2.7.0 (2022-05-11)¶
Support Python 3.11 (no wheels yet, they will only be available when Python 3.11 is RC when the ABI is stable).
2.6.0 (2022-01-10)¶
Drop Python 3.6 support.
2.5.0 (2021-12-14)¶
Add
time_machine.escape_hatch, which provides functions to bypass time-machine.Thanks to Matt Pegler for the feature request in Issue #206.
2.4.1 (2021-11-27)¶
Build musllinux wheels.
2.4.0 (2021-09-01)¶
Support Python 3.10.
2.3.1 (2021-07-13)¶
Build universal2 wheels for Python 3.8 on macOS.
2.3.0 (2021-07-05)¶
Allow passing
ticktoCoordinates.move_to()and the pytest fixture’stime_machine.move_to(). This allows freezing or unfreezing of time when travelling.
2.2.0 (2021-07-02)¶
Include type hints.
Convert C module to use PEP 489 multi-phase extension module initialization. This makes the module ready for Python sub-interpreters.
Release now includes a universal2 wheel for Python 3.9 on macOS, to work on Apple Silicon.
Stop distributing tests to reduce package size. Tests are not intended to be run outside of the tox setup in the repository. Repackagers can use GitHub’s tarballs per tag.
2.1.0 (2021-02-19)¶
Release now includes wheels for ARM on Linux.
2.0.1 (2021-01-18)¶
Prevent
ImportErroron Windows wheretime.tzset()is unavailable.
2.0.0 (2021-01-17)¶
Release now includes wheels for Windows and macOS.
Move internal calculations to use nanoseconds, avoiding a loss of precision.
After a call to
move_to(), the first function call to retrieve the current time will return exactly the destination time, copying the behaviour of the first call totravel().Add the ability to shift timezone by passing in a
ZoneInfotimezone.Remove
tz_offsetargument. This was incorrectly copied fromfreezegun. Use the new timezone mocking withZoneInfoinstead.Add pytest plugin and fixture
time_machine.Work with Windows’ different epoch.
1.3.0 (2020-12-12)¶
Support Python 3.9.
Move license from ISC to MIT License.
1.2.1 (2020-08-29)¶
Correctly return naive datetimes from
datetime.utcnow()whilst time travelling.Thanks to Søren Pilgård and Bart Van Loon for the report in Issue #52.
1.2.0 (2020-07-08)¶
Add
move_to()method to move to a different time whilst travelling. This is based on freezegun’smove_to()method.
1.1.1 (2020-06-22)¶
Move C-level
clock_gettime()andclock_gettime_ns()checks to runtime to allow distribution of macOS wheels.
1.1.0 (2020-06-08)¶
Add
shift()method to move forward in time by a delta whilst travelling. This is based on freezegun’stick()method.Thanks to Alex Subbotin for the feature in PR #27.
Fix to work when either
clock_gettime()orCLOCK_REALTIMEis not present. This happens on some Unix platforms, for example on macOS with the official Python.org installer, which is compiled against macOS 10.9.Thanks to Daniel Crowe for the fix in PR #30.
1.0.1 (2020-05-29)¶
Fix
datetime.now()behaviour with thetzargument when not time-travelling.
1.0.0 (2020-05-29)¶
First non-beta release.
Added support for
tz_offsetargument.tick=Truewill only start time ticking after the first method return that retrieves the current time.Added nestability of
travel().Support for
time.time_ns()andtime.clock_gettime_ns().
1.0.0b1 (2020-05-04)¶
First release on PyPI.