OPEN RECORD · 4 MEASURED CHECKS
Same-zone wall-time subtraction vs UTC elapsed time across DST
Subtracting datetimes sharing one ZoneInfo object gives wall-time arithmetic. Convert each instant to UTC when the required contract is elapsed time across a DST transition.
When this evidence is relevant
Investigating zoneinfo DST, datetime subtraction, elapsed time: compare the exact failure below with your public error, then check the measured scope.
Observed failure
spring_forward_elapsed_matches_utc_timeline, fall_back_elapsed_matches_utc_timeline, repeated_hour_elapsed_matches_utc_timeline
Use end.astimezone(timezone.utc) - start.astimezone(timezone.utc). Preserve fold when constructing ambiguous instants; replace(tzinfo=UTC) does not convert an instant.
America/New_York only, bundled system IANA 2025b TZif; fixed historical 2024 spring, fall, ambiguous-hour and winter inputs. This computes elapsed time between already-resolved instants, not wall-clock schedules or validation of nonexistent local times. No other zone/rule version or leap-second handling is tested.
Measured 2026-09-12T13:40:14.911993+00:00 · Linux / x86_64. Historical package versions are not current production recommendations.
Primary references
- Python 3.12 official behavior reference
- ZoneInfo data sources and fold
- IANA time zone database provenance
The complete record and all reproduction files are open. No response is required.
Complete record
- spring_forward_elapsed_matches_utc_timeline
- fall_back_elapsed_matches_utc_timeline
- repeated_hour_elapsed_matches_utc_timeline
- winter_control_elapsed_matches_utc_timeline
Full execution record and environments
{
"schema_version": 2,
"test_id": "zoneinfo-elapsed-time-v1",
"created_at": "2026-09-12T13:40:14.911993+00:00",
"environment": {
"libc": [
"glibc",
"2.39"
],
"machine": "x86_64",
"packages": {
"tzdata": {
"loader": "ZoneInfo.from_file on bundled TZif, not ambient TZPATH",
"source": "Ubuntu system tzdata /usr/share/zoneinfo",
"tzif_sha256": "e9ed07d7bee0c76a9d442d091ef1f01668fee7c4f26014c0a868b19fe6c18a95",
"version": "2025b",
"zone": "America/New_York"
}
},
"python_abi": "cpython-312-x86_64-linux-gnu",
"python_implementation": "CPython",
"python_version": "3.12.14",
"system": "Linux"
},
"fixed_environment": {
"libc": [
"glibc",
"2.39"
],
"machine": "x86_64",
"packages": {
"tzdata": {
"loader": "ZoneInfo.from_file on bundled TZif, not ambient TZPATH",
"source": "Ubuntu system tzdata /usr/share/zoneinfo",
"tzif_sha256": "e9ed07d7bee0c76a9d442d091ef1f01668fee7c4f26014c0a868b19fe6c18a95",
"version": "2025b",
"zone": "America/New_York"
}
},
"python_abi": "cpython-312-x86_64-linux-gnu",
"python_implementation": "CPython",
"python_version": "3.12.14",
"system": "Linux"
},
"status": "passed",
"failure": {
"checks": [
"winter_control_elapsed_matches_utc_timeline"
],
"error": "spring_forward_elapsed_matches_utc_timeline, fall_back_elapsed_matches_utc_timeline, repeated_hour_elapsed_matches_utc_timeline",
"failed_checks": [
"spring_forward_elapsed_matches_utc_timeline",
"fall_back_elapsed_matches_utc_timeline",
"repeated_hour_elapsed_matches_utc_timeline"
],
"mode": "broken",
"observations": [
{
"calculated_elapsed_seconds": 14400,
"end": "2024-03-10T04:00:00-04:00",
"end_utc": "2024-03-10T08:00:00+00:00",
"expected_elapsed_seconds": 10800,
"fold_end": 0,
"fold_start": 0,
"name": "spring_forward",
"start": "2024-03-10T00:00:00-05:00",
"start_utc": "2024-03-10T05:00:00+00:00",
"wall_seconds": 14400
},
{
"calculated_elapsed_seconds": 14400,
"end": "2024-11-03T04:00:00-05:00",
"end_utc": "2024-11-03T09:00:00+00:00",
"expected_elapsed_seconds": 18000,
"fold_end": 0,
"fold_start": 0,
"name": "fall_back",
"start": "2024-11-03T00:00:00-04:00",
"start_utc": "2024-11-03T04:00:00+00:00",
"wall_seconds": 14400
},
{
"calculated_elapsed_seconds": 0,
"end": "2024-11-03T01:30:00-05:00",
"end_utc": "2024-11-03T06:30:00+00:00",
"expected_elapsed_seconds": 3600,
"fold_end": 1,
"fold_start": 0,
"name": "repeated_hour",
"start": "2024-11-03T01:30:00-04:00",
"start_utc": "2024-11-03T05:30:00+00:00",
"wall_seconds": 0
},
{
"calculated_elapsed_seconds": 7200,
"end": "2024-01-15T12:00:00-05:00",
"end_utc": "2024-01-15T17:00:00+00:00",
"expected_elapsed_seconds": 7200,
"fold_end": 0,
"fold_start": 0,
"name": "winter_control",
"start": "2024-01-15T10:00:00-05:00",
"start_utc": "2024-01-15T15:00:00+00:00",
"wall_seconds": 7200
}
],
"status": "check_failed",
"exit_code": 2
},
"success": {
"checks": [
"spring_forward_elapsed_matches_utc_timeline",
"fall_back_elapsed_matches_utc_timeline",
"repeated_hour_elapsed_matches_utc_timeline",
"winter_control_elapsed_matches_utc_timeline"
],
"error": "",
"failed_checks": [],
"mode": "fixed",
"observations": [
{
"calculated_elapsed_seconds": 10800,
"end": "2024-03-10T04:00:00-04:00",
"end_utc": "2024-03-10T08:00:00+00:00",
"expected_elapsed_seconds": 10800,
"fold_end": 0,
"fold_start": 0,
"name": "spring_forward",
"start": "2024-03-10T00:00:00-05:00",
"start_utc": "2024-03-10T05:00:00+00:00",
"wall_seconds": 14400
},
{
"calculated_elapsed_seconds": 18000,
"end": "2024-11-03T04:00:00-05:00",
"end_utc": "2024-11-03T09:00:00+00:00",
"expected_elapsed_seconds": 18000,
"fold_end": 0,
"fold_start": 0,
"name": "fall_back",
"start": "2024-11-03T00:00:00-04:00",
"start_utc": "2024-11-03T04:00:00+00:00",
"wall_seconds": 14400
},
{
"calculated_elapsed_seconds": 3600,
"end": "2024-11-03T01:30:00-05:00",
"end_utc": "2024-11-03T06:30:00+00:00",
"expected_elapsed_seconds": 3600,
"fold_end": 1,
"fold_start": 0,
"name": "repeated_hour",
"start": "2024-11-03T01:30:00-04:00",
"start_utc": "2024-11-03T05:30:00+00:00",
"wall_seconds": 0
},
{
"calculated_elapsed_seconds": 7200,
"end": "2024-01-15T12:00:00-05:00",
"end_utc": "2024-01-15T17:00:00+00:00",
"expected_elapsed_seconds": 7200,
"fold_end": 0,
"fold_start": 0,
"name": "winter_control",
"start": "2024-01-15T10:00:00-05:00",
"start_utc": "2024-01-15T15:00:00+00:00",
"wall_seconds": 7200
}
],
"status": "passed",
"exit_code": 0
},
"checks": [
"spring_forward_elapsed_matches_utc_timeline",
"fall_back_elapsed_matches_utc_timeline",
"repeated_hour_elapsed_matches_utc_timeline",
"winter_control_elapsed_matches_utc_timeline"
],
"negative_controls": {
"replace-with-utc": {
"checks": [
"winter_control_elapsed_matches_utc_timeline"
],
"error": "spring_forward_elapsed_matches_utc_timeline, fall_back_elapsed_matches_utc_timeline, repeated_hour_elapsed_matches_utc_timeline",
"failed_checks": [
"spring_forward_elapsed_matches_utc_timeline",
"fall_back_elapsed_matches_utc_timeline",
"repeated_hour_elapsed_matches_utc_timeline"
],
"mode": "replace-with-utc",
"observations": [
{
"calculated_elapsed_seconds": 14400,
"end": "2024-03-10T04:00:00-04:00",
"end_utc": "2024-03-10T08:00:00+00:00",
"expected_elapsed_seconds": 10800,
"fold_end": 0,
"fold_start": 0,
"name": "spring_forward",
"start": "2024-03-10T00:00:00-05:00",
"start_utc": "2024-03-10T05:00:00+00:00",
"wall_seconds": 14400
},
{
"calculated_elapsed_seconds": 14400,
"end": "2024-11-03T04:00:00-05:00",
"end_utc": "2024-11-03T09:00:00+00:00",
"expected_elapsed_seconds": 18000,
"fold_end": 0,
"fold_start": 0,
"name": "fall_back",
"start": "2024-11-03T00:00:00-04:00",
"start_utc": "2024-11-03T04:00:00+00:00",
"wall_seconds": 14400
},
{
"calculated_elapsed_seconds": 0,
"end": "2024-11-03T01:30:00-05:00",
"end_utc": "2024-11-03T06:30:00+00:00",
"expected_elapsed_seconds": 3600,
"fold_end": 1,
"fold_start": 0,
"name": "repeated_hour",
"start": "2024-11-03T01:30:00-04:00",
"start_utc": "2024-11-03T05:30:00+00:00",
"wall_seconds": 0
},
{
"calculated_elapsed_seconds": 7200,
"end": "2024-01-15T12:00:00-05:00",
"end_utc": "2024-01-15T17:00:00+00:00",
"expected_elapsed_seconds": 7200,
"fold_end": 0,
"fold_start": 0,
"name": "winter_control",
"start": "2024-01-15T10:00:00-05:00",
"start_utc": "2024-01-15T15:00:00+00:00",
"wall_seconds": 7200
}
],
"status": "check_failed",
"exit_code": 2
}
},
"test_sha256": "9a36a795ff0ff4950d78a01cb2f63475c8c3a63f20fb6d0c72b3591b62151f7a",
"fixture_sha256": "c2b42c0008d0dab73d417d54eb1c5428ff8a9fbb99bb128ec7de4f67c0c6881a",
"requirements_fixed": "# No pip dependencies. CPython 3.11+; measured version in record.json.\n",
"scope": "America/New_York only, bundled system IANA 2025b TZif; fixed historical 2024 spring, fall, ambiguous-hour and winter inputs. This computes elapsed time between already-resolved instants, not wall-clock schedules or validation of nonexistent local times. No other zone/rule version or leap-second handling is tested.",
"sources": [
{
"title": "Python 3.12 official behavior reference",
"url": "https://docs.python.org/3.12/library/datetime.html"
},
{
"title": "ZoneInfo data sources and fold",
"url": "https://docs.python.org/3.12/library/zoneinfo.html"
},
{
"title": "IANA time zone database provenance",
"url": "https://www.iana.org/time-zones"
}
],
"artifact_sha256": {
"America-New_York.tzif": "e9ed07d7bee0c76a9d442d091ef1f01668fee7c4f26014c0a868b19fe6c18a95",
"README.md": "9d1573408aad16868cca4a67c0c020d886b761fabbfa6f8825052ada4947c5d0",
"fixture.json": "c2b42c0008d0dab73d417d54eb1c5428ff8a9fbb99bb128ec7de4f67c0c6881a",
"pipeline.py": "9a36a795ff0ff4950d78a01cb2f63475c8c3a63f20fb6d0c72b3591b62151f7a",
"probe.py": "1ef0ff6c989d3a217a48ac1031cf94c064fa68e7c944955c4b6917ccad1d96e7",
"requirements-fixed.txt": "c4f1f77998ac5b6df8b96effb120595ce438ddd2b1e25e18943c2e30e2e3c20b",
"runs/broken.stderr.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"runs/broken.stdout.log": "66acbb715fc20a632777a4835e9ca9130cbf0cc2378b14582ac3b013be37565b",
"runs/fixed.stderr.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"runs/fixed.stdout.log": "73d99e53ff61bafe1ff87b9810df1a774218fef3d34a34a2f87dbe4604709936",
"runs/probe.stderr.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"runs/probe.stdout.log": "7107707d2377aaa35301bf2cc938096c8d6b2151c5103b75fae412c5c5ac6ace",
"runs/replace-with-utc.stderr.log": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"runs/replace-with-utc.stdout.log": "a93f0ce896a5edbfac82f4175f44a0f98093b07e01c1832931c00af0ed7202dd",
"tzdata-source.json": "7c278cb31dbf0b64ac85fef7a48be640cd010afb4e708633ade6554462ee5316",
"verification-spec.json": "c22cf4b77209a0b8800bab02155e8209ffc00b2360f38c62e48af25a4a20c78c",
"verify.py": "d1cd758a372740ac2e1eb77e0dc8caa0cfea0f4875c59c7a5cec1d18ad5369bb"
},
"verification": "Actual isolated local subprocess execution; metadata/hash checks excluded from primary workload count.",
"record_id": "0ddf1e1dbd6611794bf91cd3e81cadf3be84aff249184134cfabe213af9a42dd"
}Nothing is required in return. Reader notes and result reports are optional.
Optional delivery receipt
All records and downloads are already open. Generate a receipt only if useful for your work.
Responses from readers · unverified
Only notes explicitly authorized for public display appear here. Legacy required responses and new optional notes are distinguished; neither is independent verification.
No public reader responses yet.
Have a complete reproducible solution? Submit it for review →