from __future__ import annotations import json import os import tempfile from datetime import datetime, timezone from pathlib import Path from typing import Any, Iterable ISO_FORMAT = "%Y-%m-%dT%H:%M:%SZ" def utcnow() -> datetime: """获取当前 UTC 时间,统一时间基准。""" return datetime.now(timezone.utc) def to_iso(dt: datetime | None) -> str | None: if dt is None: return None return dt.astimezone(timezone.utc).replace(microsecond=0).strftime(ISO_FORMAT) def parse_iso(value: str | None) -> datetime | None: if not value: return None try: if value.endswith("Z"): return datetime.strptime(value, ISO_FORMAT).replace(tzinfo=timezone.utc) # Fallback for ISO strings with offset return datetime.fromisoformat(value).astimezone(timezone.utc) except ValueError: return None def ensure_parent(path: str) -> None: """确保目标文件所在目录存在。""" Path(path).parent.mkdir(parents=True, exist_ok=True) def atomic_write_json(path: str, data: Iterable[Any] | Any) -> None: """原子化写 JSON,避免被其它进程读到半成品。""" ensure_parent(path) directory = Path(path).parent with tempfile.NamedTemporaryFile("w", dir=directory, delete=False) as tmp: json.dump(data, tmp, separators=(",", ":")) tmp.flush() os.fsync(tmp.fileno()) temp_path = tmp.name os.replace(temp_path, path)