Merge pull request 'fix(tz): timezone-aware datetimes — unblock TZ=Asia/Jerusalem (#210)' (#368) from worktree-agent-a29624b2a56f70804 into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m29s
G12 Leak-Guard / leak-guard (push) Successful in 5s
Lint — undefined names / undefined-names (push) Successful in 9s

This commit was merged in pull request #368.
This commit is contained in:
2026-06-30 17:25:55 +00:00
3 changed files with 24 additions and 6 deletions

View File

@@ -7,7 +7,7 @@ from __future__ import annotations
import json
import logging
from datetime import datetime
from datetime import datetime, timezone
from uuid import UUID, uuid4
from legal_mcp.services import db
@@ -38,7 +38,7 @@ async def log_action(
VALUES ($1, $2, $3, $4, $5, $6, $7)""",
uuid4(), action, case_id, document_id,
json.dumps(details or {}, ensure_ascii=False, default=str),
user, datetime.utcnow(),
user, datetime.now(timezone.utc),
)
logger.info("AUDIT: %s | case=%s | user=%s | %s", action, case_id, user,
json.dumps(details or {}, ensure_ascii=False)[:200])

View File

@@ -15,8 +15,14 @@ import os
import re
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo
from typing import Any
# Display timezone (INV-UI9): timestamps surfaced to humans are rendered in
# Israel time deterministically, regardless of the process clock (the container
# may run UTC or Asia/Jerusalem). Storage stays UTC; this is display-only.
IL_TZ = ZoneInfo("Asia/Jerusalem")
# Placeholder strings — any of these means "not yet filled"
CHAIR_POSITION_PLACEHOLDERS = (
"[ימולא ע\"י יו\"ר הוועדה]",
@@ -191,7 +197,7 @@ def parse(file_path: Path) -> dict[str, Any]:
date_str = date_match.group(1) if date_match else ""
stat = file_path.stat()
mtime_iso = datetime.fromtimestamp(stat.st_mtime).isoformat()
mtime_iso = datetime.fromtimestamp(stat.st_mtime, tz=IL_TZ).isoformat()
result: dict[str, Any] = {
"header": {
@@ -354,7 +360,7 @@ def update_chair_position(
"saved": True,
"section_id": section_id,
"preview": preview,
"timestamp": datetime.now().isoformat(),
"timestamp": datetime.now(IL_TZ).isoformat(),
}

View File

@@ -58,8 +58,9 @@ import json
import logging
import os
import sys
from datetime import datetime, timezone
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo
from statistics import mean
logger = logging.getLogger(__name__)
@@ -344,8 +345,17 @@ async def _run(args, ts: str) -> dict:
return {"dry_run": False, "grid": grid_summary, "by_block": by_block}
IL_TZ = ZoneInfo("Asia/Jerusalem")
def _ts() -> str:
return datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
"""Filename-safe report stamp in Israel time (INV-UI9: human-facing display).
This script runs on the host, which is UTC, so a process-clock stamp would
mislead the chair. We pin Asia/Jerusalem and suffix ``IL`` (instead of a bare
``Z``) so the displayed/filename time is unambiguously Israel-local.
"""
return datetime.now(IL_TZ).strftime("%Y%m%dT%H%M%S-IL")
def _write_report(result: dict, ts: str) -> tuple[Path, Path]:
@@ -356,8 +366,10 @@ def _write_report(result: dict, ts: str) -> tuple[Path, Path]:
g = result["grid"]
n = g["n_finals"]
il_now = datetime.now(IL_TZ).strftime("%Y-%m-%d %H:%M")
lines = [
f"# #208 — כיול model×effort מול הסופיים — {ts}\n",
f"> נוצר: {il_now} (שעון ישראל · Asia/Jerusalem)\n",
f"> ⚠️ **גודל-מדגם: {n} סופיים** ({', '.join(g['finals']) or ''}). "
"זוהי **עדות-כיוון, לא רגרסיה** — מעט תיקים בעלי סופי-עלוי. "
"ההמלצה אדוויזורית; ההכרעה בידי היו\"ר/המפעיל.\n",