The Clock Pattern is a Python π package that turns time into an injectable dependency π§©. Instead of scattering
datetime.now() or date.today() through application code, domain services depend on a small Clock interface. That
keeps time-sensitive logic deterministic in tests, makes timezone choices explicit, and lets production code swap clock
implementations without touching business rules.
- π₯ Installation
- π Documentation
- β‘ Quick Start
- π§© Why Inject a Clock?
- π Available Clocks
- π Timezone Behavior
- π§ͺ Testing Time-Sensitive Code
- π Real-Life Case: Christmas Detector Service
- π€ Contributing
- π License
You can install Clock Pattern using pip:
pip install clock-patternThe root README is the entry point. Deeper guides live in this repository and are linked here:
docs/README.md: Documentation hub.docs/usage/README.md: Core usage patterns and service composition.docs/timezones/README.md: Timezone behavior, UTC defaults, and date-boundary guidance.docs/testing/README.md:FixedClock,MockClock, and deterministic test patterns.
This project's DeepWiki documentation is also available for generated repository navigation.
Inject a Clock into code that needs the current time. Production code can pass a real clock, while tests can pass a
fixed or mock clock.
from clock_pattern import Clock, UtcClock
class TimestampService:
def __init__(self, *, clock: Clock) -> None:
self._clock = clock
def issued_at(self) -> str:
return self._clock.now().isoformat()
service = TimestampService(clock=UtcClock())
print(service.issued_at())Use SystemClock when
you need a specific timezone:
from clock_pattern import SystemClock
clock = SystemClock(timezone='Europe/Madrid')
print(clock.now())
# >>> 2025-06-16 15:57:26.210964+02:00Time is global state. Reading it directly from the operating system makes behavior depend on the moment a test happens to run, the machine timezone, daylight-saving transitions, and the speed of the test suite.
Clock Pattern keeps those decisions explicit:
- Domain code depends on
Clock, not on Python's global datetime functions. - Tests can choose exact dates and datetimes without monkeypatching built-in modules.
- Production wiring decides whether the application uses UTC or another timezone.
- Custom clocks can be introduced for logical time, simulation, replay, or high-precision infrastructure.
The package exposes two methods:
| Method | Returns | Typical use |
|---|---|---|
now() |
datetime |
Timestamps, expiration windows, audit fields, elapsed-time calculations. |
today() |
date |
Calendar rules, billing days, holiday checks, date-only decisions. |
The package offers several clock implementations to suit different needs:
| Clock | Import path | Purpose |
|---|---|---|
Clock |
from clock_pattern import Clock |
Abstract contract for code that needs now() or today(). |
SystemClock |
from clock_pattern import SystemClock |
Production clock backed by system time in a configured timezone. |
UtcClock |
from clock_pattern import UtcClock |
Production clock fixed to UTC. |
FixedClock |
from clock_pattern.clocks.testing import FixedClock |
Test clock that always returns the same datetime and derived date. |
MockClock |
from clock_pattern.clocks.testing import MockClock |
Test clock with prepared return values and call assertions. |
Use the top-level package for production clocks and clock_pattern.clocks.testing for test-only clocks.
SystemClock accepts either an IANA timezone string or a tzinfo instance. It stores the timezone with ZoneInfo and
uses it for both now() and today().
from datetime import UTC
from clock_pattern import SystemClock
utc_clock = SystemClock(timezone=UTC)
madrid_clock = SystemClock(timezone='Europe/Madrid')
print(utc_clock.timezone)
# >>> UTC
print(madrid_clock.timezone)
# >>> Europe/MadridUtcClock is a convenience clock for the common production choice of UTC.
today() is calculated in the clock timezone. Around midnight, SystemClock(timezone='UTC').today() and
SystemClock(timezone='America/New_York').today() may return different dates. For more details, see
docs/timezones/README.md.
Use FixedClock when the test only needs a stable instant:
from datetime import datetime
from clock_pattern.clocks.testing import FixedClock
clock = FixedClock(instant=datetime(year=2025, month=1, day=1, hour=10, minute=30))
assert clock.now().isoformat() == '2025-01-01T10:30:00+00:00'
assert clock.today().isoformat() == '2025-01-01'Use MockClock when the test also needs to prove that time was requested:
from datetime import date
from clock_pattern.clocks.testing import MockClock
clock = MockClock()
clock.prepare_today_method_return_value(today=date(year=2025, month=1, day=7))
assert clock.today() == date(year=2025, month=1, day=7)
clock.assert_today_method_was_called_once()
clock.assert_now_method_was_not_called()More testing recipes are available in docs/testing/README.md.
This service checks whether the current date falls within a Christmas holiday range. The service depends on Clock, so
production code can use UtcClock
and tests can use MockClock
without changing the service.
from datetime import date
from clock_pattern import Clock, UtcClock
from clock_pattern.clocks.testing import MockClock
class ChristmasDetectorService:
def __init__(self, *, clock: Clock) -> None:
self._clock = clock
self._christmas_start = date(year=2024, month=12, day=24)
self._christmas_end = date(year=2025, month=1, day=6)
def is_christmas(self) -> bool:
return self._christmas_start <= self._clock.today() <= self._christmas_end
clock = UtcClock()
service = ChristmasDetectorService(clock=clock)
print(service.is_christmas())
# >>> False
def test_christmas_detector_is_christmas() -> None:
clock = MockClock()
service = ChristmasDetectorService(clock=clock)
today = date(year=2024, month=12, day=25)
clock.prepare_today_method_return_value(today=today)
assert service.is_christmas() is True
clock.assert_today_method_was_called_once()
def test_christmas_detector_is_not_christmas() -> None:
clock = MockClock()
service = ChristmasDetectorService(clock=clock)
today = date(year=2025, month=1, day=7)
clock.prepare_today_method_return_value(today=today)
assert service.is_christmas() is False
clock.assert_today_method_was_called_once()We love community help! Before you open an issue or pull request, please read:
Thank you for helping make π°οΈ Clock Pattern package awesome! π
This project is licensed under the terms of the MIT license.