diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f6ba7a3f..511c0c62 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -86,6 +86,18 @@ Tests can be run with `tox`. Some basic code style tests can be run with `tox
-e flake8` and most other targets run the test suite with various supported
Python interpreters.
+Tests use the real date by default. For reproducible builds, set
+`STDNUM_TEST_DATE` to an explicit date in `YYYY-MM-DD` format, for example
+`STDNUM_TEST_DATE=2026-09-21 tox -e py312`. The same variable works with a
+direct `pytest` invocation. Invalid or empty values fail with a configuration
+error.
+
+This test-only option replaces `datetime.date.today()` during the test session;
+it does not change the host clock or the installed library. Date construction
+still returns ordinary date objects. Choose a reference date appropriate for
+the release being tested. Leave the variable unset for development and
+scheduled tests so that changes caused by the passage of time remain visible.
+
Module implementations have a couple of smaller test cases that also serve as
basic documentation of the happy flow.
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 00000000..1038c25a
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,72 @@
+# conftest.py - optional reference date for tests
+#
+# Copyright (C) 2026 Ryan Duguid
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see .
+
+"""Pytest configuration for python-stdnum."""
+
+import datetime
+import os
+from unittest.mock import patch
+
+import pytest
+
+
+pytest_plugins = ['pytester']
+
+
+def pytest_configure(config):
+ """Validate the optional reference date before collecting tests."""
+ value = os.environ.get('STDNUM_TEST_DATE')
+ config._stdnum_reference_date = None
+ if value is None:
+ return
+ try:
+ fixed_date = datetime.date.fromisoformat(value)
+ if fixed_date.isoformat() != value:
+ raise ValueError()
+ except ValueError:
+ raise pytest.UsageError('STDNUM_TEST_DATE must be a valid date in YYYY-MM-DD format')
+ config._stdnum_reference_date = fixed_date
+
+
+@pytest.fixture(scope='session', autouse=True)
+def reference_date(request):
+ """Optionally replace date.today() for the duration of the test session."""
+ fixed_date = request.config._stdnum_reference_date
+ if fixed_date is None:
+ yield
+ return
+
+ real_date = datetime.date
+
+ class DateMeta(type):
+ """Keep isinstance checks compatible with ordinary date objects."""
+
+ def __instancecheck__(cls, instance):
+ return isinstance(instance, real_date)
+
+ class ReferenceDate(real_date, metaclass=DateMeta):
+ """Return ordinary dates, overriding only the current date."""
+
+ def __new__(cls, *args, **kwargs):
+ return real_date(*args, **kwargs)
+
+ @classmethod
+ def today(cls):
+ return fixed_date
+
+ with patch.object(datetime, 'date', ReferenceDate):
+ yield
diff --git a/tests/test_reference_date.py b/tests/test_reference_date.py
new file mode 100644
index 00000000..4395db63
--- /dev/null
+++ b/tests/test_reference_date.py
@@ -0,0 +1,78 @@
+# test_reference_date.py - test the optional reference date
+#
+# Copyright (C) 2026 Ryan Duguid
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, see .
+
+"""Check the test reference date in isolated pytest processes."""
+
+from pathlib import Path
+
+import pytest
+
+
+@pytest.fixture
+def clock_suite(pytester: pytest.Pytester, monkeypatch: pytest.MonkeyPatch) -> pytest.Pytester:
+ """Install the real configuration in an isolated test directory."""
+ # These child suites test pytest configuration, not library coverage.
+ monkeypatch.delenv('COV_CORE_DATAFILE', raising=False)
+ source = Path(__file__).resolve().parent.parent / 'conftest.py'
+ pytester.makeconftest(source.read_text() + """
+import datetime
+_original_date = datetime.date
+
+def pytest_sessionfinish(session):
+ assert datetime.date is _original_date
+""")
+ pytester.makepyfile("""
+import datetime
+import os
+
+real_date = datetime.date
+real_today = datetime.date.today()
+
+def test_date():
+ value = os.environ.get('STDNUM_TEST_DATE')
+ expected = real_date.fromisoformat(value) if value is not None else real_today
+ assert datetime.date.today() == expected
+ assert datetime.date(2000, 2, 29) == real_date(2000, 2, 29)
+ assert repr(datetime.date(2000, 2, 29)) == 'datetime.date(2000, 2, 29)'
+ assert isinstance(real_date(2000, 2, 29), datetime.date)
+ assert isinstance(datetime.datetime.now().date(), datetime.date)
+ if value is None:
+ assert datetime.date is real_date
+""")
+ return pytester
+
+
+def test_default_uses_real_date(clock_suite: pytest.Pytester, monkeypatch: pytest.MonkeyPatch) -> None:
+ """No reference date leaves the date class and clock unchanged."""
+ monkeypatch.delenv('STDNUM_TEST_DATE', raising=False)
+ clock_suite.runpytest_subprocess('-q').assert_outcomes(passed=1)
+
+
+@pytest.mark.parametrize('value', ['2024-01-01', '2040-04-24', '2024-02-29'])
+def test_explicit_reference_date(clock_suite: pytest.Pytester, monkeypatch: pytest.MonkeyPatch, value: str) -> None:
+ """A requested date applies during tests and is restored afterwards."""
+ monkeypatch.setenv('STDNUM_TEST_DATE', value)
+ clock_suite.runpytest_subprocess('-q').assert_outcomes(passed=1)
+
+
+@pytest.mark.parametrize('value', ['', 'invalid', '2023-02-29', '20240101'])
+def test_invalid_reference_date(clock_suite: pytest.Pytester, monkeypatch: pytest.MonkeyPatch, value: str) -> None:
+ """Invalid dates fail with an actionable configuration error."""
+ monkeypatch.setenv('STDNUM_TEST_DATE', value)
+ result = clock_suite.runpytest_subprocess('-q')
+ assert result.ret == pytest.ExitCode.USAGE_ERROR
+ assert 'STDNUM_TEST_DATE must be a valid date in YYYY-MM-DD format' in result.stderr.str()
diff --git a/tox.ini b/tox.ini
index fec260d4..4676e83c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,6 +6,7 @@ skip_missing_interpreters = true
deps = pytest
pytest-cov
commands = pytest
+passenv = STDNUM_TEST_DATE
setenv=
PYTHONWARNINGS=all
@@ -33,6 +34,7 @@ setenv=
[testenv:mypy]
skip_install = true
deps = mypy<2.0 # required to keep Python 3.9 support
+ pytest<9 # pytest 9 syntax exceeds the Python 3.9 type-check target
types-requests
zeep
commands =