From 685811e3dacdd6c7f20f71efce3778edc7cec569 Mon Sep 17 00:00:00 2001 From: Hanna Vigil Date: Mon, 13 Jul 2026 13:38:08 -0700 Subject: [PATCH] fix: convert deploy timestamps to UTC before sending #patch `deploys add` and `update-by-uuid` captured the local wall-clock time via datetime.now() and formatted it with a hardcoded "Z" suffix, putting local time on the wire mislabeled as UTC. The backend trusts the offset, so the deploy shows up shifted by the client's UTC offset in the UI. Interpret the naive timestamp as local time and convert to UTC before formatting, so the "Z" is accurate. Covers both the default "now" and an explicit --timestamp. Co-Authored-By: Claude Opus 4.8 (1M context) --- cortexapps_cli/commands/deploys.py | 6 ++-- tests/test_deploys_timestamp.py | 47 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 tests/test_deploys_timestamp.py diff --git a/cortexapps_cli/commands/deploys.py b/cortexapps_cli/commands/deploys.py index e15173a..e91843f 100644 --- a/cortexapps_cli/commands/deploys.py +++ b/cortexapps_cli/commands/deploys.py @@ -1,5 +1,5 @@ from collections import defaultdict -from datetime import datetime +from datetime import datetime, timezone from enum import Enum import json from rich import print_json @@ -168,7 +168,7 @@ def add( if customData: data["customData"] = dict(customData) - data["timestamp"] = data["timestamp"].strftime('%Y-%m-%dT%H:%M:%SZ') + data["timestamp"] = data["timestamp"].astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ') r = client.post("api/v1/catalog/" + tag + "/deploys", data=data) print_json(data=r) @@ -236,7 +236,7 @@ def update_by_uuid( data["deployer"]["email"] = email if name: data["deployer"]["name"] = name - data["timestamp"] = data["timestamp"].strftime('%Y-%m-%dT%H:%M:%SZ') + data["timestamp"] = data["timestamp"].astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ') r = client.put("api/v1/catalog/" + tag + "/deploys/" + uuid, data=data) print_json(data=r) diff --git a/tests/test_deploys_timestamp.py b/tests/test_deploys_timestamp.py new file mode 100644 index 0000000..1d1d550 --- /dev/null +++ b/tests/test_deploys_timestamp.py @@ -0,0 +1,47 @@ +import time + +from tests.helpers.utils import * + +# Fixed-offset zone (UTC-5, no DST) so the local->UTC conversion is deterministic +# regardless of the machine/CI timezone. +_TZ = "Etc/GMT+5" + + +def _with_fixed_tz(fn): + old_tz = os.environ.get("TZ") + os.environ["TZ"] = _TZ + time.tzset() + try: + fn() + finally: + if old_tz is None: + os.environ.pop("TZ", None) + else: + os.environ["TZ"] = old_tz + time.tzset() + + +def _posted_timestamp(cli_args): + responses.add( + responses.POST, + os.getenv("CORTEX_BASE_URL") + "/api/v1/catalog/cli-test-service/deploys", + json={}, + status=200, + ) + cli(cli_args) + return json.loads(responses.calls[0].request.body)["timestamp"] + + +@responses.activate +def test_add_converts_local_timestamp_to_utc(): + """An explicit local --timestamp is converted to UTC before being sent with a Z suffix""" + def check(): + ts = _posted_timestamp([ + "deploys", "add", "-t", "cli-test-service", + "--title", "my title", "--type", "DEPLOY", + "--timestamp", "2020-01-15T10:30:00", + ]) + # 10:30 in UTC-5 is 15:30 UTC + assert ts == "2020-01-15T15:30:00Z" + + _with_fixed_tz(check)