Skip to main content

pytest-calendar-chaos

PyPI Python CI License: MIT

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

Test your Python code on leap day (Feb 29), the last day of the month, New Year's Eve, midnight and daylight saving time changes, without waiting for those dates to arrive.

A pytest plugin that runs each explicitly marked test once per tricky calendar moment (leap day, month end, year end, midnight, ISO week 53, the 2038 limit, 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(): ...

Demo: pytest-calendar-chaos catching a month-end bug

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, iso_week, y2038, 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
iso_week iso-week-53-2026-12-31, iso-week-53-new-year-2027, iso-week-1-2027-01-04
y2038 y2038-before-limit, y2038-last-valid-second, y2038-after-limit
dst computed per zone from zoneinfo for 2027 (below)

iso_week covers 2026, a year with 53 ISO weeks: Dec 31 2026 and Jan 1 2027 both fall in ISO week 53 of ISO year 2026, so code that mixes the calendar year (%Y) with the ISO week (%V) or uses %G incorrectly breaks here.

y2038 sits on the signed 32-bit time_t limit, 2038-01-19 03:14:07 UTC (2**31 - 1 seconds). These three cases are absolute UTC instants, so they are the same moment in every zone (only the local wall-clock reading changes). Python itself handles these dates; the cases help find code that stores times in 32-bit fields, such as databases, serialization formats and C extensions.

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.
  • The clock is frozen by default (tick=False). time.time() and datetime.now() never advance inside the test, so code that waits in a loop for the wall clock to pass a deadline will hang. Pass tick=True for tests that need time to move.
  • Only wall-clock time is mocked. time.monotonic() and time.perf_counter() keep running in real time, and so does the asyncio event loop clock: asyncio.sleep and asyncio.wait_for still take real time even when datetime.now() is frozen.
  • 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.4.1

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.4.1
File Size Uploaded Provenance
pytest_calendar_chaos-0.4.1.tar.gz 119.7 kB With provenance Details

Built distribution (wheel)

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

Total release size: 134.1 kB

Release files / pytest_calendar_chaos-0.4.1.tar.gz

Download URL pytest_calendar_chaos-0.4.1.tar.gz
Size 119.7 kB
Tags Source
SHA-256 checksum
How to use checksums
48c2c633d7b74fd5ed7928aeebf89f374f38302cc190ad76d7710e496e089a10
BLAKE2b-256 checksum
How to use checksums
48951cacf948324f62f75ec302622bcac0f7ecd68b68893d602b3c341f5ef47c
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 23, 2026.

Transparency log

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

Download URL pytest_calendar_chaos-0.4.1-py3-none-any.whl
Size 14.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
870aecac5f601007b45d06b3d581f22b94f8afcbb55816c71b57078b65037885
BLAKE2b-256 checksum
How to use checksums
1804bbd8e21d99c1c301963ae13792ef82d1886c3403e2513aad921ad17fc607
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 23, 2026.

Transparency log

Security for pytest-calendar-chaos 0.4.1

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.4.1.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 23, 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