Skip to main content

pytest-calendar-chaos

PyPI Python CI License: MIT

Your tests pass today. Will they pass on Feb 29?

A pytest plugin that runs each explicitly marked test once per tricky calendar moment (leap day, month end, year end, midnight, DST transitions) with the clock frozen by time-machine. When a run fails, the report tells you which scenario failed, the exact mocked time, and one command that reruns only that scenario.

import pytest


@pytest.mark.calendar_chaos(
    cases=["leap_day", "month_end", "dst"], zones=["UTC", "America/New_York"]
)
def test_subscription_renewal(): ...

A failing run looks like this (from examples/, a renewal function that breaks at month end):

-------------------------------- Calendar chaos --------------------------------
Case:      31-day month end  [month_end / month-end-jan-31-2027]
Zone:      UTC
Clock:     2027-01-31T12:00:00+00:00
Frozen:    yes (tick=False)
Reproduce: pytest "test_billing.py::test_next_renewal_is_one_month_later[month-end-jan-31-2027]"
           pytest test_billing.py --calendar-case month-end-jan-31-2027

Install

pip install pytest-calendar-chaos

Requires Python 3.10+ and pytest 8+. Clock mocking is done entirely by time-machine.

Marker

@pytest.mark.calendar_chaos(cases=[...], zones=["UTC"], tick=False) on a function, class, or module (pytestmark). The closest marker wins.

  • cases: required, non-empty list of presets: leap_day, month_end, year_end, midnight, dst (- and _ are interchangeable). Unknown names are a collection error listing the valid presets.
  • zones: optional IANA zone names, default ["UTC"]. Each fixed case is a local wall-clock time placed in each zone. An unknown zone is a collection error naming it.
  • tick: passed to time_machine.travel. Default False (frozen).
  • Combining with time-machine's own time_machine marker is a collection error.

Test IDs look like test_x[leap-day-2028] (UTC) or test_x[leap-day-2028@america-new-york]. IDs never depend on today's date, the machine, or test order, so saved reproduce commands keep working.

CLI

--calendar-case ID (repeatable) keeps only matching scenarios, by full ID (leap-day-2028@america-new-york) or base ID (leap-day-2028, every zone). Unmarked tests are unaffected. A marked test with no matching scenario is skipped. An ID that matches nothing in the whole session prints a warning at the end of the run (not shown under pytest-xdist).

--calendar-chaos-list prints the case catalog (IDs, local times, labels) and exits without running any tests. Use it to find the ID to pass to --calendar-case.

Configuration

calendar_chaos_default_zones (ini option, space separated) sets the zones used by markers that don't pass zones. Explicit zones=[...] on a marker always wins. Default: UTC.

[pytest]
calendar_chaos_default_zones = UTC America/New_York

Fixture

calendar_case returns the current Scenario (.case, .zone, .moment, .note, .id), or None for unmarked tests.

Catalog

Fixed presets use fixed years (2027 normal, 2028 leap); nothing is computed from "now".

Preset Case IDs
leap_day leap-eve-2028, leap-day-2028, leap-day-last-microsecond-2028, leap-day-after-2028
month_end month-end-jan-31-2027, month-end-feb-28-2027, month-start-mar-01-2027, month-end-apr-30-2027, month-end-feb-29-2028
year_end year-end-2027, year-end-last-microsecond-2027, new-year-2028
midnight midnight-before-2027-06-15, midnight-after-2027-06-16
dst computed per zone from zoneinfo for 2027 (below)

DST

For each zone with a transition in 2027, spring-forward gets dst-spring-before-2027 and dst-spring-after-2027 (one minute either side). Fall-back gets dst-fall-before-2027, dst-fall-first-repeat-2027 (fold=0), dst-fall-second-repeat-2027 (fold=1) and dst-fall-after-2027. IDs carry the zone suffix, e.g. dst-fall-second-repeat-2027@europe-london. Zones without DST in 2027 (UTC, Asia/Tokyo) contribute nothing; if no listed zone has DST, collection fails with a clear message. If a zone has several transitions of one kind in the year, IDs are numbered (dst-spring-2-before-2027). Failure reports add a Transition: line, for example 2027-03-14 02:00 local clocks jump to 03:00 (America/New_York).

Case IDs are a public API: adding cases is a minor release, changing or removing one is breaking.

Limitations

  • Only the clock inside the Python process is mocked. Databases, subprocesses and C extensions that read the system clock are not.
  • Fixtures with class, module or session scope are set up before the freeze and see real time. Function-scoped fixtures and the test body see the mocked time.
  • Comparing aware datetimes in a repeated hour. Per PEP 495, a fold datetime in a repeated DST hour never compares equal to a datetime in another time zone, even at the same instant. Compare calendar_case.moment.astimezone(timezone.utc) or use .timestamp().
  • Freezing exact moments in non-UTC zones, including every DST case, is tested in CI on Linux, macOS and Windows. Naive datetime.now() returning the zone's local wall time relies on time-machine setting the process time zone (TZ / time.tzset); that is tested on Linux and macOS only, because Windows has no tzset. On Windows, compare aware datetimes rather than relying on naive local time.
  • Under pytest-xdist the failure sections and the end-of-run summary work, but the "unmatched --calendar-case" warning is not printed.
  • Async tests get no special handling.

How it works

Built on time-machine (MIT, by Adam Johnson), which does all clock mocking. This plugin adds the curated catalog, automatic parametrization, and reproducible failure reports.

Comparison

  • time-machine / freezegun alone: mock the clock, but you choose the dates and write the parametrization. This plugin supplies a vetted catalog and reproducible reports on top of time-machine.
  • Hypothesis datetimes(): generates datetimes (including tricky ones) as test inputs. It does not freeze "now" for code that calls datetime.now(). The two are complementary.
  • pytest-tzshift: runs tests across a matrix of time zones and locales. This plugin varies the moment in time (and zone), not just the environment.

Contributing

See CONTRIBUTING.md. New calendar cases are very welcome.

License

MIT

Metadata

Release files for pytest-calendar-chaos 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-calendar-chaos 0.3.0
File Size Uploaded Provenance
pytest_calendar_chaos-0.3.0.tar.gz 129.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-calendar-chaos 0.3.0
File Interpreter ABI Platform
pytest_calendar_chaos-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 142.8 kB

Release files / pytest_calendar_chaos-0.3.0.tar.gz

Download URL pytest_calendar_chaos-0.3.0.tar.gz
Size 129.5 kB
Tags Source
SHA-256 checksum
How to use checksums
91fd7ae0925cea83862356b4778f97c37098e98fa3611332bccfce151478ca0b
BLAKE2b-256 checksum
How to use checksums
83e86ab48077d44f797a31eb5b820546de39c900e69e623b21736e893c8b74d0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / pytest_calendar_chaos-0.3.0-py3-none-any.whl

Download URL pytest_calendar_chaos-0.3.0-py3-none-any.whl
Size 13.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
45a4a641297eceb2df055846870bf01674640b5b0b5a08ddaf24e647a9dee04b
BLAKE2b-256 checksum
How to use checksums
0f9aac3adc876e090497289e09a3c89885ccb3e755513649c126ef32b86777df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Security for pytest-calendar-chaos 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pytest_calendar_chaos-0.3.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page