fix: 82 爬虫上游时间为 UTC,+8 小时转北京时间

上游 82 网站返回的 date 数组是 UTC,直接入库会让这批数字命名的设备时间比
实际早 8 小时。

实证(本地库):
  这批设备的 check_time 是 2026-02-06 17:11:10(那轮采集完成时刻),
  而上游原值落在 09:07:02 ~ 09:10:30,+8h 后正好是 17:07 ~ 17:10 —— 即采集
  完成前一分钟。若原值已是北京时间,则意味着这批站点在采集前 8 小时就集体
  停止上报、且整齐划一,不合常理。

- 解析后 +timedelta(hours=8)。中国无夏令时,固定偏移与 pytz 转换等价。
- 清洗 T / Z / 毫秒后缀,兼容 ISO 格式。
- 解析失败原样兜底 —— 有值但格式不认识,原样透传给入库层的 staleness_of
  再解析,与之前“拿不到时间必须为 None”的约定不冲突。
- 跨日正确:16:00 UTC 顺延至次日 00:00。

注意:本次只改后端源码,需重新打包 exe 才会在服务器生效。
This commit is contained in:
DXC
2026-09-16 09:08:05 +08:00
parent 736d644792
commit 51714a7883

View File

@ -4,7 +4,7 @@ import json
import logging import logging
from lxml import etree from lxml import etree
from config import Config from config import Config
from datetime import datetime from datetime import datetime, timedelta
CONFIG = Config.CRAWLER_CONFIG["82"] CONFIG = Config.CRAWLER_CONFIG["82"]
@ -44,9 +44,29 @@ def run_82_logic():
if data: if data:
d_list = data.get('date', []) d_list = data.get('date', [])
# 上游返回的是 UTC,需 +8 小时转北京时间。
# 实证:库里 check_time 为 2026-02-06 17:11:10(那轮采集完成时刻),
# 而同一批设备的上游原值落在 09:07~09:10,+8h 后正好是 17:07~17:10,
# 即采集完成前一分钟 —— 若原值已是北京时间,则意味着这批站点在采集
# 前 8 小时就集体停止上报,不合常理。
# 中国无夏令时,固定 +8h 与 pytz 转换等价。
# 拿不到时间时必须是 None,不能是 "N/A" —— 字符串在入库层是真值, # 拿不到时间时必须是 None,不能是 "N/A" —— 字符串在入库层是真值,
# 会把设备主表已经冻结的有效 latest_time 覆盖掉,令 offset 倒退成"从未同步" # 会把设备主表已经冻结的有效 latest_time 覆盖掉,令 offset 倒退成"从未同步"
latest = str(d_list[-1]) if d_list else None latest = None
if d_list:
raw_time_str = str(d_list[-1])
try:
# 兼容可能的 T 或 Z 等 ISO 格式,先做一下基础清洗
clean_str = raw_time_str.replace('T', ' ').replace('Z', '').split('.')[0]
dt = datetime.strptime(clean_str, "%Y-%m-%d %H:%M:%S")
# 加上 8 小时转换为北京时间
dt = dt + timedelta(hours=8)
latest = dt.strftime("%Y-%m-%d %H:%M:%S")
except Exception:
# 如果解析失败,原样返回兜底
latest = raw_time_str
data_packet['target_time'] = latest data_packet['target_time'] = latest
data_packet['value'] = f"Data Points: {len(d_list)}" data_packet['value'] = f"Data Points: {len(d_list)}"
data_packet['raw_json'] = data # 🔥 存完整JSON data_packet['raw_json'] = data # 🔥 存完整JSON