Freezegun is a simple but powerful Python library that makes it easy to test code based on the current date or time.
It lets you "freeze" time during tests to control what datetime returns. This is super useful for testing things like scheduled tasks, expiration logic, or time-based conditions—whether you're working on the backend or frontend.
In this guide, you'll use Freezegun’s main features and write clear, reliable tests for time-sensitive code.
Prerequisites
Ensure you have Python installed—version 3.13 or higher is recommended.
You should also be familiar with basic Python and testing concepts.
Step 1 — Setting up the directory
In this section, you'll create a project directory and set up Freezegun to test your Python code that depends on time.
To begin, create a new directory and navigate into it:
Then, create a virtual environment and activate it:
This command creates a virtual environment that keeps your project dependencies isolated.
Now, install Freezegun and pytest as development dependencies:
After installation, create a simple Python module with a time-dependent function. Here's the function in full:
The get_current_year() function returns the current year, is_weekend() checks if the current day is a weekend, and format_timestamp() returns a formatted current timestamp. Save this file at the root of your project.
Next, let's write the first test using Freezegun.
Step 2 — Writing your first test
Unit tests help ensure that time-dependent functions behave as expected under different conditions. Instead of waiting for specific times or dates to test your functions, you can "freeze" time using Freezegun.
Start by creating a tests directory in your project root:
Next, create an empty __init__.py file in the tests directory to make it a proper Python package:
Next, create a file named test_time_functions.py inside the tests directory and add the following test:
The freeze_time decorator or context manager temporarily changes the return value of datetime.now(), allowing you to control the date and time during the test.
Within this test, the freeze_time context manager sets the date to May 15, 2023, and we check whether get_current_year() returns 2023 as expected.
Now that the test is written, let's run it in the next step.
Step 3 — Running your tests
With your test file set up, you can run it using pytest.
To execute all tests, run:
Pytest will automatically find and run test files inside the tests directory. The output should look something like this:
The test runner successfully detected and executed the test_time_functions.py file, confirming that the test suite ran as expected.
Alongside the results, the execution time for each test case is displayed, providing insight into the performance of the test run.
You can enable verbose mode to see more details about the tests being executed:
This shows that the test_get_current_year test passed successfully, verifying that our function works correctly when the date is set to May 15, 2023.
Step 4 — Testing date-dependent functionality
As your application grows, you'll likely need to test functions with more complex time dependencies. Freezegun provides several ways to freeze time at different points, allowing you to test various scenarios.
Let's expand our test file to include more tests for date-dependent functionality:
In test_is_weekend(), the test freezes time to a Monday, Saturday, and Sunday to check if the is_weekend() function correctly identifies weekends. This is useful for features like scheduling or applying different rules on weekends.
test_format_timestamp() ensures the format_timestamp() function returns consistent output across different formats by freezing time to a specific datetime. It checks the full timestamp, a date-only format, and a time-only format—helpful for logs or user interfaces.
These tests improve reliability by removing dependence on the real system clock. Run them with:
This approach allows you to test time-dependent code comprehensively without waiting for specific dates or times to occur, making your tests reliable and repeatable.
Step 5 — Using the decorator syntax
Freezegun provides both a context manager and a decorator syntax. While we've used the context manager in previous examples, the decorator syntax can be more concise, especially when the entire test function needs to run at a specific time.
Let's update our test file to use decorator syntax:
Running these tests should produce similar results to our previous examples:
The decorator syntax is particularly useful when all test method assertions must be executed at the same frozen time. It enhances readability by clearly indicating the time context for the entire test function.
Step 6 — Advancing time during tests
Sometimes, it's important to test how your code behaves as time moves forward—like checking timeouts, scheduled events, or age-based logic. Freezegun makes this easy by letting you simulate the passage of time within your tests.
Start by creating a new file called time_travel.py with a few functions that rely on elapsed time:
This code defines two functions that rely on the current time. calculate_age figures out a person's age based on their birth date, adjusting if their birthday hasn’t passed yet this year.
The is_token_expired function checks if a token has passed its expiration time. Both depend on datetime.now(), making them perfect for testing with Freezegun to simulate time changes reliably.
Now, let's write tests that simulate time passing to check how these functions behave. Create a file called test_time_travel.py:
This test file uses Freezegun to simulate the passage of time and verify time-based logic.
In test_calculate_age(), the test freezes time to check how age changes before and after a birthday. Advancing time confirms that the function updates correctly as the date changes.
In test_token_expiry(), a token is issued at 10:00 AM. The test moves time forward to confirm it expires after 30 minutes, as expected.
When you run these tests, you'll see how Freezegun allows you to manipulate time:
The ability to advance time during tests is powerful for checking time-dependent behavior without introducing arbitrary delays that would make your tests slow and fragile.
Step 7 — Auto-tick advancement
For testing code that checks the current time repeatedly, Freezegun provides an "auto-tick" feature. This automatically advances the frozen time after each call to datetime.now(), simulating the passage of time.
Create a file called timer.py and add the following function:
This SimpleTimer class tracks elapsed time between its creation (or last reset) and the current moment. It stores the start time and calculates the difference when elapsed_seconds() is called.
Next, create a test_auto_tick.py file to test the timer function using Freezegun’s auto-tick feature:
The first test shows normal behavior where time remains frozen, so multiple calls to elapsed_seconds() always return 0.
The second test demonstrates auto-tick by setting autotickseconds=2, which advances the clock by 2 seconds every time datetime.now() is called internally.
Run the test to see auto-tick in action:
The auto-tick feature is handy for testing code that measures elapsed time or needs to simulate the gradual passage of time without manual intervention.
Since the SimpleTimer.elapsed_seconds() method calls datetime.now() internally, each call advances time automatically when using auto-tick.
Step 8 — Testing different time zones
Handling time zones correctly is often a significant challenge when working with time-dependent applications. Freezegun provides built-in support for testing with different time zones, allowing you to verify that your code works correctly across time zones.
Create a file named timezone_functions.py in your project root with time zone-aware functions:
These functions handle different time zone operations. The first gets the current time in a specific time zone, and the second checks if it's business hours (9 AM - 5 PM weekdays) in a given time zone.
Make sure to install the pytz library:
Create a file named tests/test_timezones.py for testing these time zone functions:
In this code, you add tests to verify time zone handling with Freezegun and pytz.
The test_timezone_conversion() function checks if a frozen UTC time is correctly converted to local times in New York, Tokyo, and UTC itself. This ensures time zone offsets are correctly applied.
The test_business_hours() function checks if specific times fall within business hours in different regions. At 3:30 PM UTC, it should be within working hours in New York and London, but outside of them in Tokyo.
These tests help confirm your code handles time zone logic accurately across different locations.
Run the tests to verify that the time zone handling works correctly:
When testing with time zones, the tz_offset parameter in freeze_time is crucial. It specifies the base UTC offset, allowing you to control the reference time. In the examples above, we set it to 0 (UTC) and then test how different time zones relate to that reference time.
This approach is particularly important for applications that serve users across different time zones or need to handle time zone-specific business rules.
Final thoughts
This article showed how Freezegun simplifies testing time-based Python code by giving you full control over datetime.now() in your tests. You learned how to freeze time, simulate its passage, and handle different time zones to test logic like business hours, token expirations, and scheduled tasks.
To dive deeper into advanced features and use cases, check out the official Freezegun documentation.