feat(api): 統一使用台北時區 UTC+8 (禁止 UTC)

- 新增 src/utils/timezone.py 時區工具函式
- 修改 11 個後端檔案,全部改用 now_taipei()
- 更新 HARD_RULES.md 加入時區鐵律章節
- 更新 Skills 02/04 加入時區禁令

🔴 HARD RULE: 禁止 datetime.utcnow() / datetime.now(UTC)
 正確做法: from src.utils.timezone import now_taipei

Memory: feedback_timezone_taipei.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-03-25 09:08:34 +08:00
parent 5f3271174f
commit 2a2dac865a
14 changed files with 276 additions and 49 deletions

View File

@@ -21,7 +21,7 @@ Supported Operations:
import asyncio
import time
from dataclasses import dataclass
from datetime import UTC, datetime
from src.utils.timezone import now_taipei, now_taipei_iso
from enum import Enum
from pathlib import Path
from typing import Any
@@ -376,7 +376,7 @@ class ActionExecutor:
"template": {
"metadata": {
"annotations": {
"kubectl.kubernetes.io/restartedAt": datetime.now(UTC).isoformat()
"kubectl.kubernetes.io/restartedAt": now_taipei().isoformat()
}
}
}
@@ -1060,7 +1060,7 @@ async def execute_approved_proposal(approval_id: str) -> ExecutionResult:
# Step 5: 更新狀態
new_status = "executed" if result.success else "failed"
execution_log = {
"executed_at": datetime.now(UTC).isoformat(),
"executed_at": now_taipei().isoformat(),
"success": result.success,
"stdout": result.k8s_response.get("stdout", "") if result.k8s_response else "",
"stderr": result.error or "",

View File

@@ -11,7 +11,9 @@ Phase 6: leWOOOgo Output Plugins
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import UTC, datetime
from datetime import datetime
from src.utils.timezone import now_taipei
from enum import Enum
from typing import Any
@@ -68,7 +70,7 @@ class NotificationMessage:
confidence: float | None = None
# 時間戳
timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))
timestamp: datetime = field(default_factory=lambda: now_taipei())
@property
def status_emoji(self) -> str:
@@ -117,7 +119,7 @@ class NotificationResult:
message: str
response_data: dict[str, Any] | None = None
error: str | None = None
timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))
timestamp: datetime = field(default_factory=lambda: now_taipei())
class NotificationProvider(ABC):

View File

@@ -23,9 +23,9 @@ import json
import random
import re
import time
from datetime import datetime
import httpx
from src.utils.timezone import now_taipei_iso
import structlog
from src.core.config import settings
@@ -789,7 +789,7 @@ class OpenClawService:
cache_data = {
"response": response,
"provider": provider,
"cached_at": datetime.now().isoformat(),
"cached_at": now_taipei_iso(),
}
await redis_client.set(
cache_key,