From 9db10824727c9e8d70d277e80b2334992ea96de4 Mon Sep 17 00:00:00 2001 From: Raphael Fakhri <153192858+RaphaelFakhri@users.noreply.github.com> Date: Mon, 19 Oct 2026 08:15:44 +0000 Subject: [PATCH 1/2] fix(util): Decode partition dates as UTC regardless of the host timezone decode_part_str called .astimezone(UTC) on the naive datetime returned by strptime, which Python interprets as local time. On hosts east of UTC the Monday partition date became the previous Sunday, so cleanup dropped partitions a week early and partition-split lightweight deletes matched no rows. --- snuba/util.py | 2 +- tests/test_util.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/snuba/util.py b/snuba/util.py index 6d6b73f27d4..0a08b8611dd 100644 --- a/snuba/util.py +++ b/snuba/util.py @@ -113,7 +113,7 @@ def get_re(format: Sequence[PartSegment]) -> Pattern[Any]: if date_str and retention_days: return Part( part_str, - datetime.strptime(date_str, "%Y-%m-%d").astimezone(UTC), + datetime.strptime(date_str, "%Y-%m-%d").replace(tzinfo=UTC), int(retention_days), ) diff --git a/tests/test_util.py b/tests/test_util.py index 08f48076b3b..ce9d0be175e 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,4 +1,11 @@ +import time +from collections.abc import Generator +from datetime import UTC, datetime + +import pytest + from snuba.clickhouse.escaping import escape_alias, escape_identifier +from snuba.util import PartSegment, decode_part_str def test_escape_identifier() -> None: @@ -21,3 +28,21 @@ def test_escape_alias() -> None: assert escape_alias("foo") == "foo" assert escape_alias("foo.bar") == "`foo.bar`" assert escape_alias("foo:bar") == "`foo:bar`" + + +@pytest.fixture +def local_timezone(monkeypatch: pytest.MonkeyPatch) -> Generator[None]: + # A zone east of UTC, where local midnight falls on the previous UTC day. + monkeypatch.setenv("TZ", "Asia/Tokyo") + time.tzset() + yield + monkeypatch.undo() + time.tzset() + + +@pytest.mark.usefixtures("local_timezone") +def test_decode_part_str_ignores_local_timezone() -> None: + part = decode_part_str("(90,'2026-10-12')", [PartSegment.RETENTION_DAYS, PartSegment.DATE]) + assert part.date == datetime(2026, 10, 12, tzinfo=UTC) + assert part.date.weekday() == 0 + assert part.retention_days == 90 From adced4b878d48aad64dd510e8806b30d5dc90845 Mon Sep 17 00:00:00 2001 From: Elio Di Nino <52972357+ElioDiNino@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:41:09 -0700 Subject: [PATCH 2/2] ref(tests): Use monkeypatch.context in local_timezone fixture --- tests/test_util.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_util.py b/tests/test_util.py index ce9d0be175e..b0ae674c79d 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -33,10 +33,10 @@ def test_escape_alias() -> None: @pytest.fixture def local_timezone(monkeypatch: pytest.MonkeyPatch) -> Generator[None]: # A zone east of UTC, where local midnight falls on the previous UTC day. - monkeypatch.setenv("TZ", "Asia/Tokyo") - time.tzset() - yield - monkeypatch.undo() + with monkeypatch.context() as m: + m.setenv("TZ", "Asia/Tokyo") + time.tzset() + yield time.tzset()