Pendulum DateTime arithmetic and timezone shifts frozen with time-machine: working versions (tested 2026-10-15)
Freeze the clock with time-machine to test Pendulum month-end clamping, DST-safe day additions and timezone shifts fully offline.
passed6 passed · 0 failed2026-10-15 16:03 UTC- Status
- passed
- Last passed
- 2026-10-15 16:03 UTC
- Tests
- 6 / 6
- Language
- Python
- Runtime
- Python 3.13.16
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 38764699834
The code
Exactly the files that passed. Your agent can pull them with get_recipe("pendulum-time-machine-freeze") (install), or see them on GitHub.
pendulum==3.3.0 time-machine==3.5.1 pytest==9.1.1
import pendulum
from pendulum import DateTime
def days_until(target: DateTime) -> int:
"""Whole days between now (in target's timezone) and target."""
return (target - pendulum.now(target.tz)).days
def to_tz(dt: DateTime, tz: str) -> DateTime:
"""The same instant, expressed in another timezone."""
return dt.in_timezone(tz)
def add_months(anchor: DateTime, months: int = 1) -> DateTime:
"""Add calendar months, clamping to the last valid day."""
return anchor.add(months=months)
import datetime as dt
import time
import pendulum
import time_machine
from clock import add_months, days_until, to_tz
def test_freeze_pins_pendulum_now():
frozen = pendulum.datetime(2024, 1, 15, 12, 0, tz="UTC")
with time_machine.travel(frozen, tick=False):
assert pendulum.now("UTC") == frozen
assert days_until(frozen.add(days=10)) == 10
assert days_until(frozen) == 0
def test_naive_input_kind_changes_meaning():
naive = dt.datetime(2024, 1, 15, 12, 0)
with time_machine.travel(naive, tick=False):
assert dt.datetime.now(dt.timezone.utc).replace(tzinfo=None) == naive
with time_machine.travel("2024-01-15 12:00:00", tick=False):
assert pendulum.now().naive() == naive
def test_tick_flag_controls_flow():
with time_machine.travel("2024-01-15 12:00:00", tick=False):
assert pendulum.now("UTC") == pendulum.now("UTC")
with time_machine.travel("2024-01-15 12:00:00", tick=True):
first = pendulum.now("UTC")
time.sleep(0.01)
assert pendulum.now("UTC") > first
def test_add_days_keeps_wall_clock_across_dst():
frozen = pendulum.datetime(2021, 3, 13, 12, 0, tz="America/New_York")
with time_machine.travel(frozen, tick=False):
now = pendulum.now("America/New_York")
assert now.add(days=1).hour == 12
assert now.add(days=1).offset_hours == -4.0
assert now.add(hours=24).hour == 13
def test_add_months_clamps_to_month_end():
with time_machine.travel(pendulum.datetime(2024, 1, 31, tz="UTC"), tick=False):
assert add_months(pendulum.now("UTC")) == pendulum.datetime(2024, 2, 29, tz="UTC")
assert add_months(pendulum.now("UTC"), -1) == pendulum.datetime(2023, 12, 31, tz="UTC")
def test_in_timezone_keeps_instant():
frozen = pendulum.datetime(2021, 8, 1, 12, 0, tz="Europe/Paris")
with time_machine.travel(frozen, tick=False):
now = pendulum.now("Europe/Paris")
ny = to_tz(now, "America/New_York")
assert ny.hour == 6
assert ny == now
assert ny.timestamp() == now.timestamp()
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.
Related stacks
APScheduler job triggers tested deterministically with time-machine
Compute CronTrigger and IntervalTrigger fire times without a running scheduler, and test them offline by freezing the clock with time-machine.
Arrow date shifting and humanize with clocks frozen by time-machine
Shift Arrow dates by calendar units, humanize relative times, and pin the clock with time-machine so the output is deterministic offline.
Recurring schedules with dateutil rrule and zoneinfo DST handling, frozen with time-machine
Build daily wall-clock schedules in a named timezone with dateutil rrule and zoneinfo, then test DST gaps, overlaps and naive datetimes offline with time-machine.