import versions
+# REPL 可打印以确认已烧录新逻辑:version_util.UTIL_API 应为 4
+UTIL_API = 4
+
def _char_is_digit(ch):
try:
return len(ch) == 1 and ch in "0123456789"
+def _all_digits(s):
+ if not s:
+ return False
+ for i in range(len(s)):
+ if not _char_is_digit(s[i]):
+ return False
+ return True
+
+
+def _digits_only(s):
+ out = ""
+ for i in range(len(str(s))):
+ c = str(s)[i]
+ if _char_is_digit(c):
+ out += c
+ return out
+
+
+def _pad2(x):
+ """MicroPython 兼容:不用 zfill,仅补成两位。"""
+ s = str(x)
+ if len(s) == 1:
+ return "0" + s
+ return s
+
+
+def _format_time_parts(y, mo, d, hh, mi):
+ try:
+ return "{}_{}_{}_{}_{}".format(
+ str(y), _pad2(mo), _pad2(d), _pad2(hh), _pad2(mi)
+ )
+ except Exception:
+ return ""
+
+
+def _parse_compact_time(s):
+ """纯数字 YYYYMMDDHHmmss / YYYYMMDDHHmm,只取到分钟(前 12 位)。"""
+ if not s or not _all_digits(s):
+ return ""
+ if len(s) >= 12:
+ return _format_time_parts(s[0:4], s[4:6], s[6:8], s[8:10], s[10:12])
+ return ""
+
+
+def _time_parts_from_underscore_list(parts):
+ if len(parts) < 5:
+ return ""
+ return _format_time_parts(parts[0], parts[1], parts[2], parts[3], parts[4])
+
+
def _build_time_token_sanitize(s):
- #ASCII 兜底:不依赖 isdigit/逐字符迭代,仅 replace + split。
if not s:
return ""
- out = str(s).strip()
+ raw = str(s).strip()
+ compact = _parse_compact_time(_digits_only(raw))
+ if compact:
+ return compact
+
+ out = raw
for a, b in ((" ", "_"), ("-", "_"), (":", "_"), (".", "_"), ("/", "_")):
out = out.replace(a, b)
while "__" in out:
out = out.replace("__", "_")
out = out.strip("_")
- parts = [p for p in out.split("_") if p]
- if len(parts) >= 5:
- y, mo, d, hh, mi = parts[0], parts[1], parts[2], parts[3], parts[4]
- try:
- return "{}_{}_{}_{}_{}".format(
- y, mo.zfill(2), d.zfill(2), hh.zfill(2), mi.zfill(2)
- )
- except Exception:
- pass
- return out
+ parts = []
+ for p in out.split("_"):
+ if p:
+ parts.append(p)
+ token = _time_parts_from_underscore_list(parts)
+ if token:
+ return token
+ if len(parts) >= 6 and _all_digits(parts[0]):
+ token = _format_time_parts(parts[0], parts[1], parts[2], parts[3], parts[4])
+ if token:
+ return token
+ return ""
def _build_time_token():
- #从 versions.Build_Time 解析,输出 yyyy_MM_dd_HH_mm(不含秒)。
- #Build_Time="2026-05-20 17:39:09" --> yyyy_MM_dd_HH_mm
+ # 从 versions.Build_Time 解析为 yyyy_MM_dd_HH_mm(不含秒)
try:
s = str(getattr(versions, "Build_Time", "") or "").strip().strip('"').strip("'")
except Exception:
if not s:
return ""
+ # 优先:抽出全部数字再解析(最稳,兼容 2026-05-21 15:08:47 / 2026_05_21_15_08_47)
+ token = _parse_compact_time(_digits_only(s))
+ if token:
+ return token
+
try:
- # 已是下划线格式:2026_05_19_13_48_10
- if all((_char_is_digit(c) or c == "_") for c in s):
- parts = [p for p in s.split("_") if p]
- if len(parts) >= 5:
- y, mo, d, hh, mi = parts[0], parts[1], parts[2], parts[3], parts[4]
- return "{}_{}_{}_{}_{}".format(
- y, mo.zfill(2), d.zfill(2), hh.zfill(2), mi.zfill(2)
- )
-
- # 常见:2026-05-19 13:48:10
+ if ("_" in s) and (not ("-" in s)) and (not (" " in s)) and (not (":" in s)):
+ parts = []
+ for p in s.split("_"):
+ if p:
+ parts.append(p)
+ token = _time_parts_from_underscore_list(parts)
+ if token:
+ return token
+
while " " in s:
s = s.replace(" ", " ")
if " " in s:
date_part, time_part = s.split(" ", 1)
y, mo, d = date_part.replace("/", "-").split("-")[:3]
t = time_part.strip().split(":")
- hh = t[0].zfill(2) if len(t) > 0 else "00"
- mi = t[1].zfill(2) if len(t) > 1 else "00"
- return "{}_{}_{}_{}_{}".format(y, mo.zfill(2), d.zfill(2), hh, mi)
+ hh = t[0] if len(t) > 0 else "00"
+ mi = t[1] if len(t) > 1 else "00"
+ token = _format_time_parts(y, mo, d, hh, mi)
+ if token:
+ return token
except Exception:
pass
return _build_time_token_sanitize(s)
+def _normalize_time_token(bt):
+ if not bt:
+ return ""
+ token = _parse_compact_time(_digits_only(bt))
+ if token:
+ return token
+ token = _build_time_token_sanitize(str(bt))
+ if token:
+ return token
+ parts = []
+ for p in str(bt).split("_"):
+ if p:
+ parts.append(p)
+ return _time_parts_from_underscore_list(parts) or ""
+
+
def get_unified_version():
"""统一版本号: [PRODUCT_NAME]_[Version]_[build_time]"""
try:
ver = str(getattr(versions, "Version", "") or "").strip()
except Exception:
ver = ""
- # 对外展示统一为小写 v(k8_v1.2_xxx),与 product_*.ini 中 VERSION=V1.2 兼容
- if len(ver) >= 2 and ver[0] == "V" and ver[1].isdigit():
+ if len(ver) >= 2 and ver[0] == "V" and _char_is_digit(ver[1]):
ver = "v" + ver[1:]
- bt = _build_time_token()
+ bt = _normalize_time_token(_build_time_token())
if bt:
return "{}_{}_{}".format(pn, ver, bt)
return "{}_{}".format(pn, ver)
+def debug_version_info():
+ try:
+ raw = str(getattr(versions, "Build_Time", "") or "")
+ except Exception:
+ raw = ""
+ digits = _digits_only(raw)
+ bt = _normalize_time_token(_build_time_token())
+ return raw, digits, bt, get_unified_version()
+
+
def get_tts_version(bt_suffix=""):
- """本地 TTS 播报用统一版本号,可选拼接蓝牙模块版本。"""
base = get_unified_version()
if not bt_suffix:
return base