Files
parsedmarc/tests/tzutil.py
T
cdda5dae62 Fix failure-report timestamp skew on non-UTC hosts in ES/OpenSearch/Splunk outputs (#812)
* Fix failure-report timestamp skew on non-UTC hosts in ES/OS/Splunk sinks

arrival_date_utc is a UTC wall-clock string (generated in
parse_failure_report via an aware-UTC strftime), but elastic.py,
opensearch.py, and splunk.py parsed it back into a naive datetime and
called .timestamp(), which per the Python docs interprets naive values
as local time (https://docs.python.org/3/library/datetime.html#datetime.datetime.timestamp).
On any non-UTC host the epoch stored as the ES/OpenSearch arrival_date
field, used in the failure-report dedup match query, and sent as the
Splunk HEC event time was therefore off by the host's UTC offset
(verified -3600 s under TZ=Europe/Warsaw in January).

Add an assume_utc keyword to human_timestamp_to_datetime() /
human_timestamp_to_unix_timestamp() that attaches timezone.utc to naive
parses, and use it at the three arrival_date_utc call sites. Aware
inputs (explicit offsets) are unaffected; all other callers keep the
existing local-time semantics, whose round-trip with timestamp_to_human
is self-consistent on a single host (the broader local-time output
question is tracked separately in issue #811 bug 2).

The three new sink regression tests fail on the unfixed code
(verified by stashing the source changes) and force TZ=Europe/Warsaw
via time.tzset() so they catch the skew even on UTC CI runners.

Fixes half of https://github.com/domainaware/parsedmarc/issues/811.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Deduplicate TZ-forcing test boilerplate; fix unix-timestamp docstring

Extract the repeated TZ=Europe/Warsaw + time.tzset() setup/cleanup from
the four timestamp regression tests into a shared tests/tzutil.py
force_tz() helper, and correct human_timestamp_to_unix_timestamp()'s
docstring, which said the return type was float while the function
returns int.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: MISAPOR LAB <misapor@lab.misapor.pl>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Sean Whalen <44679+seanthegeek@users.noreply.github.com>
2026-07-09 19:45:34 -04:00

31 lines
920 B
Python

"""Shared timezone-forcing helper for timestamp regression tests."""
import os
import time
import unittest
def force_tz(testcase: unittest.TestCase, tz: str = "Europe/Warsaw") -> None:
"""Set the process timezone to *tz* for the duration of *testcase*.
Registers a cleanup that restores the previous ``TZ`` value.
Requires POSIX ``time.tzset()``; guard callers with
``@unittest.skipUnless(hasattr(time, "tzset"), ...)``.
The default zone is fixed and non-UTC (UTC+1 in January, UTC+2 in
summer), so code that wrongly interprets a UTC wall-clock string as
local time produces a shifted epoch under it.
"""
old_tz = os.environ.get("TZ")
os.environ["TZ"] = tz
time.tzset()
def restore() -> None:
if old_tz is None:
os.environ.pop("TZ", None)
else:
os.environ["TZ"] = old_tz
time.tzset()
testcase.addCleanup(restore)