From f790519765ca3d66f71d750fdf8f4ad92cf5a8eb Mon Sep 17 00:00:00 2001 From: marco Date: Wed, 17 Jun 2026 23:06:05 +0800 Subject: [PATCH] =?UTF-8?q?fix(cps):=20=E6=97=B6=E5=BA=8F=E5=88=86?= =?UTF-8?q?=E6=A1=B6=E5=AF=B9=20naive=20datetime=20=E6=8C=89=20UTC=20?= =?UTF-8?q?=E5=85=9C=E5=BA=95(=E6=9C=AC=E5=9C=B0=20SQLite=20=E9=94=99?= =?UTF-8?q?=E6=A1=B6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clicked_at.astimezone 对 naive(SQLite 读回无 tz)会按系统时区解释→分桶偏移; naive 先按 UTC 兜底再转北京,与 queries._as_utc 约定一致。生产 PG(tz-aware)本就正确。 Co-Authored-By: Claude Opus 4.8 --- app/admin/repositories/cps.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/admin/repositories/cps.py b/app/admin/repositories/cps.py index a71e16b..4790f93 100644 --- a/app/admin/repositories/cps.py +++ b/app/admin/repositories/cps.py @@ -394,7 +394,10 @@ def group_click_timeseries( agg: dict = {} # bucket_key -> {vp, vu(set), cp, cu(set)} for clicked_at, event_type, ip, ua in rows: - bj = clicked_at.astimezone(_BJ_TZ) + # naive(本地 SQLite 读回无 tz)按 UTC 兜底,再转北京——与 queries._as_utc 约定一致, + # 否则 astimezone 会把 UTC 瞬刻当系统本地时区解释,分桶整体偏移。 + ca = clicked_at if clicked_at.tzinfo else clicked_at.replace(tzinfo=timezone.utc) + bj = ca.astimezone(_BJ_TZ) key = bj.strftime("%Y-%m-%d") if granularity == "day" else bj.hour b = agg.setdefault(key, {"vp": 0, "vu": set(), "cp": 0, "cu": set()}) uid = f"{ip or ''}|{ua or ''}"